Coverage for drivers/lvutil.py : 45%

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 mgtVolActivated = False
431 try:
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)
507 # Wipe any fs signature
508 try:
509 util.wipefs(dev)
510 except util.CommandException as inst:
511 raise xs_errors.XenError('WipefsFailure', opterr='device %s' % dev) # from inst
513 if not (dev == rootdev):
514 try:
515 cmd_lvm([CMD_PVCREATE, "-ff", "-y", "--metadatasize", "10M", dev])
516 except util.CommandException as inst:
517 raise xs_errors.XenError('LVMPartCreate',
518 opterr='error is %d' % inst.code)
520 # Create VG on first device
521 try:
522 cmd_lvm([CMD_VGCREATE, "--metadatasize", "10M", vgname, rootdev])
523 except:
524 raise xs_errors.XenError('LVMGroupCreate')
526 # Then add any additional devs into the VG
527 for dev in root.split(',')[1:]:
528 try:
529 cmd_lvm([CMD_VGEXTEND, vgname, dev])
530 except util.CommandException as inst:
531 # One of the PV args failed, delete SR
532 try:
533 cmd_lvm([CMD_VGREMOVE, vgname])
534 except:
535 pass
536 raise xs_errors.XenError('LVMGroupCreate')
538 try:
539 cmd_lvm([CMD_VGCHANGE, "-an", vgname])
540 except util.CommandException as inst:
541 raise xs_errors.XenError('LVMUnMount', opterr='errno is %d' % inst.code)
543 # End block
545def removeVG(root, vgname):
546 # Check PVs match VG
547 try:
548 for dev in root.split(','):
549 txt = cmd_lvm([CMD_PVS, dev])
550 if txt.find(vgname) == -1:
551 raise xs_errors.XenError('LVMNoVolume', \
552 opterr='volume is %s' % vgname)
553 except util.CommandException as inst:
554 raise xs_errors.XenError('PVSfailed', \
555 opterr='error is %d' % inst.code)
557 try:
558 cmd_lvm([CMD_VGREMOVE, vgname])
560 for dev in root.split(','):
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):
575 "activate or deactivate VG 'path'"
576 val = "n"
577 if active:
578 val = "y"
579 text = cmd_lvm([CMD_VGCHANGE, "-a" + val, path])
582@lvmretry
583def create(name, size, vgname, tag=None, size_in_percentage=None):
584 if size_in_percentage:
585 cmd = [CMD_LVCREATE, "-n", name, "-l", size_in_percentage, vgname]
586 else:
587 size_mb = size // (1024 * 1024)
588 cmd = [CMD_LVCREATE, "-n", name, "-L", str(size_mb), vgname]
589 if tag:
590 cmd.extend(["--addtag", tag])
592 cmd.extend(['-W', 'n'])
593 cmd_lvm(cmd)
596def remove(path, config_param=None):
597 # see deactivateNoRefcount()
598 for i in range(LVM_FAIL_RETRIES): 598 ↛ 606line 598 didn't jump to line 606, because the loop on line 598 didn't complete
599 try:
600 _remove(path, config_param)
601 break
602 except util.CommandException as e:
603 if i >= LVM_FAIL_RETRIES - 1:
604 raise
605 util.SMlog("*** lvremove failed on attempt #%d" % i)
606 _lvmBugCleanup(path)
609@lvmretry
610def _remove(path, config_param=None):
611 CONFIG_TAG = "--config"
612 cmd = [CMD_LVREMOVE, "-f", path]
613 if config_param:
614 cmd.extend([CONFIG_TAG, "devices{" + config_param + "}"])
615 ret = cmd_lvm(cmd)
618@lvmretry
619def rename(path, newName):
620 cmd_lvm([CMD_LVRENAME, path, newName], pread_func=util.pread)
623@lvmretry
624def setReadonly(path, readonly):
625 val = "r"
626 if not readonly:
627 val += "w"
628 ret = cmd_lvm([CMD_LVCHANGE, path, "-p", val], pread_func=util.pread)
631def exists(path):
632 (rc, stdout, stderr) = cmd_lvm([CMD_LVS, "--noheadings", path], pread_func=util.doexec)
633 return rc == 0
636@lvmretry
637def setSize(path, size, confirm):
638 sizeMB = size // (1024 * 1024)
639 if confirm:
640 cmd_lvm([CMD_LVRESIZE, "-L", str(sizeMB), path], util.pread3, "y\n")
641 else:
642 cmd_lvm([CMD_LVRESIZE, "-L", str(sizeMB), path], pread_func=util.pread)
645@lvmretry
646def setHidden(path, hidden=True):
647 opt = "--addtag"
648 if not hidden:
649 opt = "--deltag"
650 cmd_lvm([CMD_LVCHANGE, opt, LV_TAG_HIDDEN, path])
653@lvmretry
654def _activate(path):
655 cmd = [CMD_LVCHANGE, "-ay", path]
656 cmd_lvm(cmd)
657 if not _checkActive(path):
658 raise util.CommandException(-1, str(cmd), "LV not activated")
661def activateNoRefcount(path, refresh):
662 _activate(path)
663 if refresh: 663 ↛ 665line 663 didn't jump to line 665, because the condition on line 663 was never true
664 # Override slave mode lvm.conf for this command
665 os.environ['LVM_SYSTEM_DIR'] = MASTER_LVM_CONF
666 text = cmd_lvm([CMD_LVCHANGE, "--refresh", path])
667 mapperDevice = path[5:].replace("-", "--").replace("/", "-")
668 cmd = [CMD_DMSETUP, "table", mapperDevice]
669 ret = util.pread(cmd)
670 util.SMlog("DM table for %s: %s" % (path, ret.strip()))
671 # Restore slave mode lvm.conf
672 os.environ['LVM_SYSTEM_DIR'] = DEF_LVM_CONF
675def deactivateNoRefcount(path):
676 # LVM has a bug where if an "lvs" command happens to run at the same time
677 # as "lvchange -an", it might hold the device in use and cause "lvchange
678 # -an" to fail. Thus, we need to retry if "lvchange -an" fails. Worse yet,
679 # the race could lead to "lvchange -an" starting to deactivate (removing
680 # the symlink), failing to "dmsetup remove" the device, and still returning
681 # success. Thus, we need to check for the device mapper file existence if
682 # "lvchange -an" returns success.
683 for i in range(LVM_FAIL_RETRIES): 683 ↛ 691line 683 didn't jump to line 691, because the loop on line 683 didn't complete
684 try:
685 _deactivate(path)
686 break
687 except util.CommandException:
688 if i >= LVM_FAIL_RETRIES - 1:
689 raise
690 util.SMlog("*** lvchange -an failed on attempt #%d" % i)
691 _lvmBugCleanup(path)
694@lvmretry
695def _deactivate(path):
696 text = cmd_lvm([CMD_LVCHANGE, "-an", path])
699def _checkActive(path):
700 if util.pathexists(path):
701 return True
703 util.SMlog("_checkActive: %s does not exist!" % path)
704 symlinkExists = os.path.lexists(path)
705 util.SMlog("_checkActive: symlink exists: %s" % symlinkExists)
707 mapperDeviceExists = False
708 mapperDevice = path[5:].replace("-", "--").replace("/", "-")
709 cmd = [CMD_DMSETUP, "status", mapperDevice]
710 try:
711 ret = util.pread2(cmd)
712 mapperDeviceExists = True
713 util.SMlog("_checkActive: %s: %s" % (mapperDevice, ret))
714 except util.CommandException:
715 util.SMlog("_checkActive: device %s does not exist" % mapperDevice)
717 mapperPath = "/dev/mapper/" + mapperDevice
718 mapperPathExists = util.pathexists(mapperPath)
719 util.SMlog("_checkActive: path %s exists: %s" % \
720 (mapperPath, mapperPathExists))
722 if mapperDeviceExists and mapperPathExists and not symlinkExists: 722 ↛ 724line 722 didn't jump to line 724, because the condition on line 722 was never true
723 # we can fix this situation manually here
724 try:
725 util.SMlog("_checkActive: attempt to create the symlink manually.")
726 os.symlink(mapperPath, path)
727 except OSError as e:
728 util.SMlog("ERROR: failed to symlink!")
729 if e.errno != errno.EEXIST:
730 raise
731 if util.pathexists(path):
732 util.SMlog("_checkActive: created the symlink manually")
733 return True
735 return False
738def _lvmBugCleanup(path):
739 # the device should not exist at this point. If it does, this was an LVM
740 # bug, and we manually clean up after LVM here
741 mapperDevice = path[5:].replace("-", "--").replace("/", "-")
742 mapperPath = "/dev/mapper/" + mapperDevice
744 nodeExists = False
745 cmd_st = [CMD_DMSETUP, "status", mapperDevice]
746 cmd_rm = [CMD_DMSETUP, "remove", mapperDevice]
747 cmd_rf = [CMD_DMSETUP, "remove", mapperDevice, "--force"]
749 try:
750 util.pread(cmd_st, expect_rc=1)
751 except util.CommandException as e:
752 if e.code == 0: 752 ↛ 755line 752 didn't jump to line 755, because the condition on line 752 was never false
753 nodeExists = True
755 if not util.pathexists(mapperPath) and not nodeExists:
756 return
758 util.SMlog("_lvmBugCleanup: seeing dm file %s" % mapperPath)
760 # destroy the dm device
761 if nodeExists: 761 ↛ 786line 761 didn't jump to line 786, because the condition on line 761 was never false
762 util.SMlog("_lvmBugCleanup: removing dm device %s" % mapperDevice)
763 for i in range(LVM_FAIL_RETRIES): 763 ↛ 786line 763 didn't jump to line 786, because the loop on line 763 didn't complete
764 try:
765 util.pread2(cmd_rm)
766 break
767 except util.CommandException as e:
768 if i < LVM_FAIL_RETRIES - 1:
769 util.SMlog("Failed on try %d, retrying" % i)
770 try:
771 util.pread(cmd_st, expect_rc=1)
772 util.SMlog("_lvmBugCleanup: dm device {}"
773 " removed".format(mapperDevice)
774 )
775 break
776 except:
777 cmd_rm = cmd_rf
778 time.sleep(1)
779 else:
780 # make sure the symlink is still there for consistency
781 if not os.path.lexists(path): 781 ↛ 784line 781 didn't jump to line 784, because the condition on line 781 was never false
782 os.symlink(mapperPath, path)
783 util.SMlog("_lvmBugCleanup: restored symlink %s" % path)
784 raise e
786 if util.pathexists(mapperPath):
787 os.unlink(mapperPath)
788 util.SMlog("_lvmBugCleanup: deleted devmapper file %s" % mapperPath)
790 # delete the symlink
791 if os.path.lexists(path):
792 os.unlink(path)
793 util.SMlog("_lvmBugCleanup: deleted symlink %s" % path)
796# mdpath is of format /dev/VG-SR-UUID/MGT
797# or in other words /VG_LOCATION/VG_PREFIXSR-UUID/MDVOLUME_NAME
798def ensurePathExists(mdpath):
799 if not os.path.exists(mdpath):
800 vgname = mdpath.split('/')[2]
801 lvmCache = lvmcache.LVMCache(vgname)
802 lvmCache.activateNoRefcount(MDVOLUME_NAME)
805def removeDevMapperEntry(path, strict=True):
806 try:
807 # remove devmapper entry using dmsetup
808 cmd = [CMD_DMSETUP, "remove", path]
809 cmd_lvm(cmd)
810 return True
811 except Exception as e:
812 if not strict:
813 cmd = [CMD_DMSETUP, "status", path]
814 try:
815 util.pread(cmd, expect_rc=1)
816 return True
817 except:
818 pass # Continuining will fail and log the right way
819 ret = util.pread2(["lsof", path])
820 util.SMlog("removeDevMapperEntry: dmsetup remove failed for file %s " \
821 "with error %s, and lsof ret is %s." % (path, str(e), ret))
822 return False