Hide keyboard shortcuts

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# 

18 

19import traceback 

20import re 

21import os 

22import errno 

23import time 

24 

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 

33 

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" 

55 

56MAX_OPERATION_DURATION = 15 

57 

58LVM_SIZE_INCREMENT = 4 * 1024 * 1024 

59LV_TAG_HIDDEN = "hidden" 

60LVM_FAIL_RETRIES = 10 

61 

62MASTER_LVM_CONF = '/etc/lvm/master' 

63DEF_LVM_CONF = '/etc/lvm' 

64 

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}) 

72 

73LVM_COMMANDS = VG_COMMANDS.union(PV_COMMANDS, LV_COMMANDS, DM_COMMANDS) 

74 

75LVM_LOCK = 'lvm' 

76 

77 

78def extract_vgname(str_in): 

79 """Search for and return a VG name 

80 

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. 

83 

84 Input: 

85 str_in -- (str) string to search for a VG name 

86 in the format specified above. 

87 

88 Return: 

89 vgname -- if found -> (str) 

90 if not found -> None 

91 

92 Raise: 

93 TypeError 

94 """ 

95 

96 if not util.is_string(str_in): 

97 raise TypeError("'str_in' not of type 'str'.") 

98 

99 i = str_in.find(VG_PREFIX) 

100 prefix = VG_PREFIX 

101 

102 if i == -1: 

103 i = str_in.find(EXT_PREFIX) 

104 prefix = EXT_PREFIX 

105 

106 uuid_start = i + len(prefix) 

107 re_obj = util.match_uuid(str_in[uuid_start:]) 

108 

109 if i != -1 and re_obj: 

110 return prefix + re_obj.group(0) # vgname 

111 

112 return None 

113 

114LVM_RETRY_ERRORS = [ 

115 "Incorrect checksum in metadata area header" 

116] 

117 

118 

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 

126 

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 

135 

136 time.sleep(1) 

137 

138 decorated.__name__ = func.__name__ 

139 return decorated 

140 

141 

142def cmd_lvm(cmd, pread_func=util.pread2, *args): 

143 """ Construct and run the appropriate lvm command. 

144 

145 For PV commands, the full path to the device is required. 

146 

147 Input: 

148 cmd -- (list) lvm command 

149 cmd[0] -- (str) lvm command name 

150 cmd[1:] -- (str) lvm command parameters 

151 

152 pread_func -- (function) the flavor of util.pread to use 

153 to execute the lvm command 

154 Default: util.pread2() 

155 

156 *args -- extra arguments passed to cmd_lvm will be passed 

157 to 'pread_func' 

158 

159 Return: 

160 stdout -- (str) stdout after running the lvm command. 

161 

162 Raise: 

163 util.CommandException 

164 """ 

165 

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 

172 

173 lvm_cmd, lvm_args = cmd[0], cmd[1:] 

174 

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 

178 

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 

183 

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() 

188 

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))) 

191 

192 return stdout 

193 

194 

195class LVInfo: 

196 name = "" 

197 size = 0 

198 active = False 

199 open = False 

200 hidden = False 

201 readonly = False 

202 

203 def __init__(self, name): 

204 self.name = name 

205 

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) 

210 

211 

212def _checkVG(vgname): 

213 try: 

214 cmd_lvm([CMD_VGS, "--readonly", vgname]) 

215 return True 

216 except: 

217 return False 

218 

219 

220def _checkPV(pvname): 

221 try: 

222 cmd_lvm([CMD_PVS, pvname]) 

223 return True 

224 except: 

225 return False 

226 

227 

228def _checkLV(path): 

229 try: 

230 cmd_lvm([CMD_LVDISPLAY, path]) 

231 return True 

232 except: 

233 return False 

234 

235 

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) 

243 

244 

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') 

263 

264 

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') 

283 

284 

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 "" 

301 

302 

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 [] 

316 

317 

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 "" 

333 

334 

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 

352 

353 

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) 

378 

379 for val in VGs: 

380 entry = dom.createElement('SR') 

381 element.appendChild(entry) 

382 

383 subentry = dom.createElement("UUID") 

384 entry.appendChild(subentry) 

385 textnode = dom.createTextNode(val) 

386 subentry.appendChild(textnode) 

387 

388 subentry = dom.createElement("Devlist") 

389 entry.appendChild(subentry) 

390 textnode = dom.createTextNode(VGs[val]) 

391 subentry.appendChild(textnode) 

392 

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) 

398 

399 if includeMetadata: 

400 metadataVDI = None 

401 

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 

413 

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) 

421 

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) 

427 

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) 

441 

442 return dom.toprettyxml() 

443 

444 

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) 

458 

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) 

463 

464 

465def createVG(root, vgname): 

466 systemroot = util.getrootdev() 

467 rootdev = root.split(',')[0] 

468 

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) 

478 

479 f = _openExclusive(dev, True) 

480 os.close(f) 

481 

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 

487 

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) 

494 

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') 

500 

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') 

512 

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) 

517 

518 # End block 

519 

520def removeVG(root, vgname): 

521 # Check PVs match VG 

522 try: 

523 for dev in root.split(','): 

524 txt = cmd_lvm([CMD_PVS, dev]) 

525 if txt.find(vgname) == -1: 

526 raise xs_errors.XenError('LVMNoVolume', \ 

527 opterr='volume is %s' % vgname) 

528 except util.CommandException as inst: 

529 raise xs_errors.XenError('PVSfailed', \ 

530 opterr='error is %d' % inst.code) 

531 

532 try: 

533 cmd_lvm([CMD_VGREMOVE, vgname]) 

534 

535 for dev in root.split(','): 

536 cmd_lvm([CMD_PVREMOVE, dev]) 

537 except util.CommandException as inst: 

538 raise xs_errors.XenError('LVMDelete', \ 

539 opterr='errno is %d' % inst.code) 

540 

541 

542def resizePV(dev): 

543 try: 

544 cmd_lvm([CMD_PVRESIZE, dev]) 

545 except util.CommandException as inst: 

546 util.SMlog("Failed to grow the PV, non-fatal") 

547 

548 

549def setActiveVG(path, active, config=None): 

550 "activate or deactivate VG 'path'" 

551 val = "n" 

552 if active: 

553 val = "y" 

554 cmd = [CMD_VGCHANGE, "-a" + val, path] 

555 if config: 

556 cmd.append("--config") 

557 cmd.append(config) 

558 cmd_lvm(cmd) 

559 

560 

561@lvmretry 

562def create(name, size, vgname, tag=None, size_in_percentage=None): 

563 if size_in_percentage: 

564 cmd = [CMD_LVCREATE, "-n", name, "-l", size_in_percentage, vgname] 

565 else: 

566 size_mb = size // (1024 * 1024) 

567 cmd = [CMD_LVCREATE, "-n", name, "-L", str(size_mb), vgname] 

568 if tag: 

569 cmd.extend(["--addtag", tag]) 

570 

571 cmd.extend(['-W', 'n']) 

572 cmd_lvm(cmd) 

573 

574 

575def remove(path, config_param=None): 

576 # see deactivateNoRefcount() 

577 for i in range(LVM_FAIL_RETRIES): 577 ↛ 585line 577 didn't jump to line 585, because the loop on line 577 didn't complete

578 try: 

579 _remove(path, config_param) 

580 break 

581 except util.CommandException as e: 

582 if i >= LVM_FAIL_RETRIES - 1: 

583 raise 

584 util.SMlog("*** lvremove failed on attempt #%d" % i) 

585 _lvmBugCleanup(path) 

586 

587 

588@lvmretry 

589def _remove(path, config_param=None): 

590 CONFIG_TAG = "--config" 

591 cmd = [CMD_LVREMOVE, "-f", path] 

592 if config_param: 

593 cmd.extend([CONFIG_TAG, "devices{" + config_param + "}"]) 

594 ret = cmd_lvm(cmd) 

595 

596 

597@lvmretry 

598def rename(path, newName): 

599 cmd_lvm([CMD_LVRENAME, path, newName], pread_func=util.pread) 

600 

601 

602@lvmretry 

603def setReadonly(path, readonly): 

604 val = "r" 

605 if not readonly: 

606 val += "w" 

607 ret = cmd_lvm([CMD_LVCHANGE, path, "-p", val], pread_func=util.pread) 

608 

609 

610def exists(path): 

611 (rc, stdout, stderr) = cmd_lvm([CMD_LVS, "--noheadings", path], pread_func=util.doexec) 

612 return rc == 0 

613 

614 

615@lvmretry 

616def setSize(path, size, confirm): 

617 sizeMB = size // (1024 * 1024) 

618 if confirm: 

619 cmd_lvm([CMD_LVRESIZE, "-L", str(sizeMB), path], util.pread3, "y\n") 

620 else: 

621 cmd_lvm([CMD_LVRESIZE, "-L", str(sizeMB), path], pread_func=util.pread) 

622 

623 

624@lvmretry 

625def setHidden(path, hidden=True): 

626 opt = "--addtag" 

627 if not hidden: 

628 opt = "--deltag" 

629 cmd_lvm([CMD_LVCHANGE, opt, LV_TAG_HIDDEN, path]) 

630 

631 

632@lvmretry 

633def _activate(path): 

634 cmd = [CMD_LVCHANGE, "-ay", path] 

635 cmd_lvm(cmd) 

636 if not _checkActive(path): 

637 raise util.CommandException(-1, str(cmd), "LV not activated") 

638 

639 

640def activateNoRefcount(path, refresh): 

641 _activate(path) 

642 if refresh: 642 ↛ 644line 642 didn't jump to line 644, because the condition on line 642 was never true

643 # Override slave mode lvm.conf for this command 

644 os.environ['LVM_SYSTEM_DIR'] = MASTER_LVM_CONF 

645 text = cmd_lvm([CMD_LVCHANGE, "--refresh", path]) 

646 mapperDevice = path[5:].replace("-", "--").replace("/", "-") 

647 cmd = [CMD_DMSETUP, "table", mapperDevice] 

648 with Fairlock("devicemapper"): 

649 ret = util.pread(cmd) 

650 util.SMlog("DM table for %s: %s" % (path, ret.strip())) 

651 # Restore slave mode lvm.conf 

652 os.environ['LVM_SYSTEM_DIR'] = DEF_LVM_CONF 

653 

654 

655def deactivateNoRefcount(path): 

656 # LVM has a bug where if an "lvs" command happens to run at the same time 

657 # as "lvchange -an", it might hold the device in use and cause "lvchange 

658 # -an" to fail. Thus, we need to retry if "lvchange -an" fails. Worse yet, 

659 # the race could lead to "lvchange -an" starting to deactivate (removing 

660 # the symlink), failing to "dmsetup remove" the device, and still returning 

661 # success. Thus, we need to check for the device mapper file existence if 

662 # "lvchange -an" returns success. 

663 for i in range(LVM_FAIL_RETRIES): 663 ↛ 671line 663 didn't jump to line 671, because the loop on line 663 didn't complete

664 try: 

665 _deactivate(path) 

666 break 

667 except util.CommandException: 

668 if i >= LVM_FAIL_RETRIES - 1: 

669 raise 

670 util.SMlog("*** lvchange -an failed on attempt #%d" % i) 

671 _lvmBugCleanup(path) 

672 

673 

674@lvmretry 

675def _deactivate(path): 

676 text = cmd_lvm([CMD_LVCHANGE, "-an", path]) 

677 

678 

679def _checkActive(path): 

680 if util.pathexists(path): 

681 return True 

682 

683 util.SMlog("_checkActive: %s does not exist!" % path) 

684 symlinkExists = os.path.lexists(path) 

685 util.SMlog("_checkActive: symlink exists: %s" % symlinkExists) 

686 

687 mapperDeviceExists = False 

688 mapperDevice = path[5:].replace("-", "--").replace("/", "-") 

689 cmd = [CMD_DMSETUP, "status", mapperDevice] 

690 try: 

691 with Fairlock("devicemapper"): 

692 ret = util.pread2(cmd) 

693 mapperDeviceExists = True 

694 util.SMlog("_checkActive: %s: %s" % (mapperDevice, ret)) 

695 except util.CommandException: 

696 util.SMlog("_checkActive: device %s does not exist" % mapperDevice) 

697 

698 mapperPath = "/dev/mapper/" + mapperDevice 

699 mapperPathExists = util.pathexists(mapperPath) 

700 util.SMlog("_checkActive: path %s exists: %s" % \ 

701 (mapperPath, mapperPathExists)) 

702 

703 if mapperDeviceExists and mapperPathExists and not symlinkExists: 703 ↛ 705line 703 didn't jump to line 705, because the condition on line 703 was never true

704 # we can fix this situation manually here 

705 try: 

706 util.SMlog("_checkActive: attempt to create the symlink manually.") 

707 os.symlink(mapperPath, path) 

708 except OSError as e: 

709 util.SMlog("ERROR: failed to symlink!") 

710 if e.errno != errno.EEXIST: 

711 raise 

712 if util.pathexists(path): 

713 util.SMlog("_checkActive: created the symlink manually") 

714 return True 

715 

716 return False 

717 

718 

719def _lvmBugCleanup(path): 

720 # the device should not exist at this point. If it does, this was an LVM 

721 # bug, and we manually clean up after LVM here 

722 mapperDevice = path[5:].replace("-", "--").replace("/", "-") 

723 mapperPath = "/dev/mapper/" + mapperDevice 

724 

725 nodeExists = False 

726 cmd_st = [CMD_DMSETUP, "status", mapperDevice] 

727 cmd_rm = [CMD_DMSETUP, "remove", mapperDevice] 

728 cmd_rf = [CMD_DMSETUP, "remove", mapperDevice, "--force"] 

729 

730 try: 

731 with Fairlock("devicemapper"): 

732 util.pread(cmd_st, expect_rc=1) 

733 except util.CommandException as e: 

734 if e.code == 0: 734 ↛ 737line 734 didn't jump to line 737, because the condition on line 734 was never false

735 nodeExists = True 

736 

737 if not util.pathexists(mapperPath) and not nodeExists: 

738 return 

739 

740 util.SMlog("_lvmBugCleanup: seeing dm file %s" % mapperPath) 

741 

742 # destroy the dm device 

743 if nodeExists: 743 ↛ 770line 743 didn't jump to line 770, because the condition on line 743 was never false

744 util.SMlog("_lvmBugCleanup: removing dm device %s" % mapperDevice) 

745 for i in range(LVM_FAIL_RETRIES): 745 ↛ 770line 745 didn't jump to line 770, because the loop on line 745 didn't complete

746 try: 

747 with Fairlock("devicemapper"): 

748 util.pread2(cmd_rm) 

749 break 

750 except util.CommandException as e: 

751 if i < LVM_FAIL_RETRIES - 1: 

752 util.SMlog("Failed on try %d, retrying" % i) 

753 try: 

754 with Fairlock("devicemapper"): 

755 util.pread(cmd_st, expect_rc=1) 

756 util.SMlog("_lvmBugCleanup: dm device {}" 

757 " removed".format(mapperDevice) 

758 ) 

759 break 

760 except: 

761 cmd_rm = cmd_rf 

762 time.sleep(1) 

763 else: 

764 # make sure the symlink is still there for consistency 

765 if not os.path.lexists(path): 765 ↛ 768line 765 didn't jump to line 768, because the condition on line 765 was never false

766 os.symlink(mapperPath, path) 

767 util.SMlog("_lvmBugCleanup: restored symlink %s" % path) 

768 raise e 

769 

770 if util.pathexists(mapperPath): 

771 os.unlink(mapperPath) 

772 util.SMlog("_lvmBugCleanup: deleted devmapper file %s" % mapperPath) 

773 

774 # delete the symlink 

775 if os.path.lexists(path): 

776 os.unlink(path) 

777 util.SMlog("_lvmBugCleanup: deleted symlink %s" % path) 

778 

779 

780# mdpath is of format /dev/VG-SR-UUID/MGT 

781# or in other words /VG_LOCATION/VG_PREFIXSR-UUID/MDVOLUME_NAME 

782def ensurePathExists(mdpath): 

783 if not os.path.exists(mdpath): 

784 vgname = mdpath.split('/')[2] 

785 lvmCache = lvmcache.LVMCache(vgname) 

786 lvmCache.activateNoRefcount(MDVOLUME_NAME) 

787 

788 

789def removeDevMapperEntry(path, strict=True): 

790 try: 

791 # remove devmapper entry using dmsetup 

792 cmd = [CMD_DMSETUP, "remove", path] 

793 cmd_lvm(cmd) 

794 return True 

795 except Exception as e: 

796 if not strict: 

797 cmd = [CMD_DMSETUP, "status", path] 

798 try: 

799 with Fairlock("devicemapper"): 

800 util.pread(cmd, expect_rc=1) 

801 return True 

802 except: 

803 pass # Continuining will fail and log the right way 

804 ret = util.pread2(["lsof", path]) 

805 util.SMlog("removeDevMapperEntry: dmsetup remove failed for file %s " \ 

806 "with error %s, and lsof ret is %s." % (path, str(e), ret)) 

807 return False