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, 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 ret = util.pread(cmd)
674 util.SMlog("DM table for %s: %s" % (path, ret.strip()))
675 # Restore slave mode lvm.conf
676 os.environ['LVM_SYSTEM_DIR'] = DEF_LVM_CONF
679def deactivateNoRefcount(path):
680 # LVM has a bug where if an "lvs" command happens to run at the same time
681 # as "lvchange -an", it might hold the device in use and cause "lvchange
682 # -an" to fail. Thus, we need to retry if "lvchange -an" fails. Worse yet,
683 # the race could lead to "lvchange -an" starting to deactivate (removing
684 # the symlink), failing to "dmsetup remove" the device, and still returning
685 # success. Thus, we need to check for the device mapper file existence if
686 # "lvchange -an" returns success.
687 for i in range(LVM_FAIL_RETRIES): 687 ↛ 695line 687 didn't jump to line 695, because the loop on line 687 didn't complete
688 try:
689 _deactivate(path)
690 break
691 except util.CommandException:
692 if i >= LVM_FAIL_RETRIES - 1:
693 raise
694 util.SMlog("*** lvchange -an failed on attempt #%d" % i)
695 _lvmBugCleanup(path)
698@lvmretry
699def _deactivate(path):
700 text = cmd_lvm([CMD_LVCHANGE, "-an", path])
703def _checkActive(path):
704 if util.pathexists(path):
705 return True
707 util.SMlog("_checkActive: %s does not exist!" % path)
708 symlinkExists = os.path.lexists(path)
709 util.SMlog("_checkActive: symlink exists: %s" % symlinkExists)
711 mapperDeviceExists = False
712 mapperDevice = path[5:].replace("-", "--").replace("/", "-")
713 cmd = [CMD_DMSETUP, "status", mapperDevice]
714 try:
715 ret = util.pread2(cmd)
716 mapperDeviceExists = True
717 util.SMlog("_checkActive: %s: %s" % (mapperDevice, ret))
718 except util.CommandException:
719 util.SMlog("_checkActive: device %s does not exist" % mapperDevice)
721 mapperPath = "/dev/mapper/" + mapperDevice
722 mapperPathExists = util.pathexists(mapperPath)
723 util.SMlog("_checkActive: path %s exists: %s" % \
724 (mapperPath, mapperPathExists))
726 if mapperDeviceExists and mapperPathExists and not symlinkExists: 726 ↛ 728line 726 didn't jump to line 728, because the condition on line 726 was never true
727 # we can fix this situation manually here
728 try:
729 util.SMlog("_checkActive: attempt to create the symlink manually.")
730 os.symlink(mapperPath, path)
731 except OSError as e:
732 util.SMlog("ERROR: failed to symlink!")
733 if e.errno != errno.EEXIST:
734 raise
735 if util.pathexists(path):
736 util.SMlog("_checkActive: created the symlink manually")
737 return True
739 return False
742def _lvmBugCleanup(path):
743 # the device should not exist at this point. If it does, this was an LVM
744 # bug, and we manually clean up after LVM here
745 mapperDevice = path[5:].replace("-", "--").replace("/", "-")
746 mapperPath = "/dev/mapper/" + mapperDevice
748 nodeExists = False
749 cmd_st = [CMD_DMSETUP, "status", mapperDevice]
750 cmd_rm = [CMD_DMSETUP, "remove", mapperDevice]
751 cmd_rf = [CMD_DMSETUP, "remove", mapperDevice, "--force"]
753 try:
754 util.pread(cmd_st, expect_rc=1)
755 except util.CommandException as e:
756 if e.code == 0: 756 ↛ 759line 756 didn't jump to line 759, because the condition on line 756 was never false
757 nodeExists = True
759 if not util.pathexists(mapperPath) and not nodeExists:
760 return
762 util.SMlog("_lvmBugCleanup: seeing dm file %s" % mapperPath)
764 # destroy the dm device
765 if nodeExists: 765 ↛ 790line 765 didn't jump to line 790, because the condition on line 765 was never false
766 util.SMlog("_lvmBugCleanup: removing dm device %s" % mapperDevice)
767 for i in range(LVM_FAIL_RETRIES): 767 ↛ 790line 767 didn't jump to line 790, because the loop on line 767 didn't complete
768 try:
769 util.pread2(cmd_rm)
770 break
771 except util.CommandException as e:
772 if i < LVM_FAIL_RETRIES - 1:
773 util.SMlog("Failed on try %d, retrying" % i)
774 try:
775 util.pread(cmd_st, expect_rc=1)
776 util.SMlog("_lvmBugCleanup: dm device {}"
777 " removed".format(mapperDevice)
778 )
779 break
780 except:
781 cmd_rm = cmd_rf
782 time.sleep(1)
783 else:
784 # make sure the symlink is still there for consistency
785 if not os.path.lexists(path): 785 ↛ 788line 785 didn't jump to line 788, because the condition on line 785 was never false
786 os.symlink(mapperPath, path)
787 util.SMlog("_lvmBugCleanup: restored symlink %s" % path)
788 raise e
790 if util.pathexists(mapperPath):
791 os.unlink(mapperPath)
792 util.SMlog("_lvmBugCleanup: deleted devmapper file %s" % mapperPath)
794 # delete the symlink
795 if os.path.lexists(path):
796 os.unlink(path)
797 util.SMlog("_lvmBugCleanup: deleted symlink %s" % path)
800# mdpath is of format /dev/VG-SR-UUID/MGT
801# or in other words /VG_LOCATION/VG_PREFIXSR-UUID/MDVOLUME_NAME
802def ensurePathExists(mdpath):
803 if not os.path.exists(mdpath):
804 vgname = mdpath.split('/')[2]
805 lvmCache = lvmcache.LVMCache(vgname)
806 lvmCache.activateNoRefcount(MDVOLUME_NAME)
809def removeDevMapperEntry(path, strict=True):
810 try:
811 # remove devmapper entry using dmsetup
812 cmd = [CMD_DMSETUP, "remove", path]
813 cmd_lvm(cmd)
814 return True
815 except Exception as e:
816 if not strict:
817 cmd = [CMD_DMSETUP, "status", path]
818 try:
819 util.pread(cmd, expect_rc=1)
820 return True
821 except:
822 pass # Continuining will fail and log the right way
823 ret = util.pread2(["lsof", path])
824 util.SMlog("removeDevMapperEntry: dmsetup remove failed for file %s " \
825 "with error %s, and lsof ret is %s." % (path, str(e), ret))
826 return False