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 constants import EXT_PREFIX, VG_LOCATION, VG_PREFIX 

30import lvmcache 

31import srmetadata 

32 

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" 

54 

55MAX_OPERATION_DURATION = 15 

56 

57LVM_SIZE_INCREMENT = 4 * 1024 * 1024 

58LV_TAG_HIDDEN = "hidden" 

59LVM_FAIL_RETRIES = 10 

60 

61MASTER_LVM_CONF = '/etc/lvm/master' 

62DEF_LVM_CONF = '/etc/lvm' 

63 

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

71 

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

73 

74LVM_LOCK = 'lvm' 

75 

76 

77def extract_vgname(str_in): 

78 """Search for and return a VG name 

79 

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. 

82 

83 Input: 

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

85 in the format specified above. 

86 

87 Return: 

88 vgname -- if found -> (str) 

89 if not found -> None 

90 

91 Raise: 

92 TypeError 

93 """ 

94 

95 if not util.is_string(str_in): 

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

97 

98 i = str_in.find(VG_PREFIX) 

99 prefix = VG_PREFIX 

100 

101 if i == -1: 

102 i = str_in.find(EXT_PREFIX) 

103 prefix = EXT_PREFIX 

104 

105 uuid_start = i + len(prefix) 

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

107 

108 if i != -1 and re_obj: 

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

110 

111 return None 

112 

113LVM_RETRY_ERRORS = [ 

114 "Incorrect checksum in metadata area header" 

115] 

116 

117def calcSizeLV(size: int) -> int: 

118 return util.roundup(LVM_SIZE_INCREMENT, size) 

119 

120 

121def lvmretry(func): 

122 def check_exception(exception): 

123 retry = False 

124 for error in LVM_RETRY_ERRORS: 

125 if error in exception.reason: 

126 retry = True 

127 return retry 

128 

129 def decorated(*args, **kwargs): 

130 for i in range(LVM_FAIL_RETRIES): 130 ↛ exitline 130 didn't return from function 'decorated', because the loop on line 130 didn't complete

131 try: 

132 return func(*args, **kwargs) 

133 except util.CommandException as ce: 

134 retry = check_exception(ce) 

135 if not retry or (i == LVM_FAIL_RETRIES - 1): 

136 raise 

137 

138 time.sleep(1) 

139 

140 decorated.__name__ = func.__name__ 

141 return decorated 

142 

143 

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

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

146 

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

148 

149 Input: 

150 cmd -- (list) lvm command 

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

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

153 

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

155 to execute the lvm command 

156 Default: util.pread2() 

157 

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

159 to 'pread_func' 

160 

161 Return: 

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

163 

164 Raise: 

165 util.CommandException 

166 """ 

167 

168 if type(cmd) is not list: 

169 util.SMlog("CMD_LVM: Argument 'cmd' not of type 'list'") 

170 return None 

171 if not len(cmd): 

172 util.SMlog("CMD_LVM: 'cmd' list is empty") 

173 return None 

174 

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

176 

177 if lvm_cmd not in LVM_COMMANDS: 

178 util.SMlog("CMD_LVM: '{}' is not a valid lvm command".format(lvm_cmd)) 

179 return None 

180 

181 for arg in lvm_args: 

182 if not util.is_string(arg): 

183 util.SMlog("CMD_LVM: Not all lvm arguments are of type 'str'") 

184 return None 

185 

186 with Fairlock("devicemapper"): 

187 start_time = time.time() 

188 stdout = pread_func([os.path.join(LVM_BIN, lvm_cmd)] + lvm_args, * args) 

189 end_time = time.time() 

190 

191 if (end_time - start_time > MAX_OPERATION_DURATION): 

192 util.SMlog("***** Long LVM call of '%s' took %s" % (lvm_cmd, (end_time - start_time))) 

193 

194 return stdout 

195 

196 

197class LVInfo: 

198 name = "" 

199 size = 0 

200 active = False 

201 open = False 

202 hidden = False 

203 readonly = False 

204 

205 def __init__(self, name): 

206 self.name = name 

207 

208 def toString(self): 

209 return "%s, size=%d, active=%s, open=%s, hidden=%s, ro=%s" % \ 

210 (self.name, self.size, self.active, self.open, self.hidden, \ 

211 self.readonly) 

212 

213 

214def _checkVG(vgname): 

215 try: 

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

217 return True 

218 except: 

219 return False 

220 

221 

222def _checkPV(pvname): 

223 try: 

224 cmd_lvm([CMD_PVS, pvname]) 

225 return True 

226 except: 

227 return False 

228 

229 

230def _checkLV(path): 

231 try: 

232 cmd_lvm([CMD_LVDISPLAY, path]) 

233 return True 

234 except: 

235 return False 

236 

237 

238def _getLVsize(path): 

239 try: 

240 lines = cmd_lvm([CMD_LVDISPLAY, "-c", path]).split(':') 

241 return int(lines[6]) * 512 

242 except: 

243 raise xs_errors.XenError('VDIUnavailable', \ 

244 opterr='no such VDI %s' % path) 

245 

246 

247def _getVGstats(vgname): 

248 try: 

249 text = cmd_lvm([CMD_VGS, "--noheadings", "--nosuffix", 

250 "--units", "b", vgname], 

251 pread_func=util.pread).split() 

252 size = int(text[5]) 

253 freespace = int(text[6]) 

254 utilisation = size - freespace 

255 stats = {} 

256 stats['physical_size'] = size 

257 stats['physical_utilisation'] = utilisation 

258 stats['freespace'] = freespace 

259 return stats 

260 except util.CommandException as inst: 

261 raise xs_errors.XenError('VDILoad', \ 

262 opterr='rvgstats failed error is %d' % inst.code) 

263 except ValueError: 

264 raise xs_errors.XenError('VDILoad', opterr='rvgstats failed') 

265 

266 

267def _getPVstats(dev): 

268 try: 

269 text = cmd_lvm([CMD_PVS, "--noheadings", "--nosuffix", 

270 "--units", "b", dev], 

271 pread_func=util.pread).split() 

272 size = int(text[4]) 

273 freespace = int(text[5]) 

274 utilisation = size - freespace 

275 stats = {} 

276 stats['physical_size'] = size 

277 stats['physical_utilisation'] = utilisation 

278 stats['freespace'] = freespace 

279 return stats 

280 except util.CommandException as inst: 

281 raise xs_errors.XenError('VDILoad', \ 

282 opterr='pvstats failed error is %d' % inst.code) 

283 except ValueError: 

284 raise xs_errors.XenError('VDILoad', opterr='rvgstats failed') 

285 

286 

287# Retrieves the UUID of the SR that corresponds to the specified Physical 

288# Volume (pvname). Each element in prefix_list is checked whether it is a 

289# prefix of Volume Groups that correspond to the specified PV. If so, the 

290# prefix is stripped from the matched VG name and the remainder is returned 

291# (effectively the SR UUID). If no match if found, the empty string is 

292# returned. 

293# E.g. 

294# PV VG Fmt Attr PSize PFree 

295# /dev/sda4 VG_XenStorage-some-hex-value lvm2 a- 224.74G 223.73G 

296# will return "some-hex-value". 

297def _get_sr_uuid(pvname, prefix_list): 

298 try: 

299 return match_VG(cmd_lvm([CMD_PVS, "--noheadings", 

300 "-o", "vg_name", pvname]), prefix_list) 

301 except: 

302 return "" 

303 

304 

305# Retrieves the names of the Physical Volumes which are used by the specified 

306# Volume Group 

307# e.g. 

308# PV VG Fmt Attr PSize PFree 

309# /dev/sda4 VG_XenStorage-some-hex-value lvm2 a- 224.74G 223.73G 

310# will return "/dev/sda4" when given the argument "VG_XenStorage-some-hex-value". 

311def get_pv_for_vg(vgname): 

312 try: 

313 result = cmd_lvm([CMD_PVS, "--noheadings", 

314 '-S', 'vg_name=%s' % vgname, '-o', 'name']) 

315 return [x.strip() for x in result.splitlines()] 

316 except util.CommandException: 

317 return [] 

318 

319 

320# Tries to match any prefix contained in prefix_list in s. If matched, the 

321# remainder string is returned, else the empty string is returned. E.g. if s is 

322# "VG_XenStorage-some-hex-value" and prefix_list contains "VG_XenStorage-", 

323# "some-hex-value" is returned. 

324# 

325# TODO Items in prefix_list are expected to be found at the beginning of the 

326# target string, though if any of them is found inside it a match will be 

327# produced. This could be remedied by making the regular expression more 

328# specific. 

329def match_VG(s, prefix_list): 

330 for val in prefix_list: 

331 regex = re.compile(val) 

332 if regex.search(s, 0): 

333 return s.split(val)[1] 

334 return "" 

335 

336 

337# Retrieves the devices an SR is composed of. A dictionary is returned, indexed 

338# by the SR UUID, where each SR UUID is mapped to a comma-separated list of 

339# devices. Exceptions are ignored. 

340def scan_srlist(prefix, root): 

341 VGs = {} 

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

343 try: 

344 sr_uuid = _get_sr_uuid(dev, [prefix]).strip(' \n') 

345 if len(sr_uuid): 

346 if sr_uuid in VGs: 

347 VGs[sr_uuid] += ",%s" % dev 

348 else: 

349 VGs[sr_uuid] = dev 

350 except Exception as e: 

351 util.logException("exception (ignored): %s" % e) 

352 continue 

353 return VGs 

354 

355 

356# Converts an SR list to an XML document with the following structure: 

357# <SRlist> 

358# <SR> 

359# <UUID>...</UUID> 

360# <Devlist>...</Devlist> 

361# <size>...</size> 

362# <!-- If includeMetadata is set to True, the following additional nodes 

363# are supplied. --> 

364# <name_label>...</name_label> 

365# <name_description>...</name_description> 

366# <pool_metadata_detected>...</pool_metadata_detected> 

367# </SR> 

368# 

369# <SR>...</SR> 

370# </SRlist> 

371# 

372# Arguments: 

373# VGs: a dictionary containing the SR UUID to device list mappings 

374# prefix: the prefix that if prefixes the SR UUID the VG is produced 

375# includeMetadata (optional): include additional information 

376def srlist_toxml(VGs, prefix, includeMetadata=False): 

377 dom = xml.dom.minidom.Document() 

378 element = dom.createElement("SRlist") 

379 dom.appendChild(element) 

380 

381 for val in VGs: 

382 entry = dom.createElement('SR') 

383 element.appendChild(entry) 

384 

385 subentry = dom.createElement("UUID") 

386 entry.appendChild(subentry) 

387 textnode = dom.createTextNode(val) 

388 subentry.appendChild(textnode) 

389 

390 subentry = dom.createElement("Devlist") 

391 entry.appendChild(subentry) 

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

393 subentry.appendChild(textnode) 

394 

395 subentry = dom.createElement("size") 

396 entry.appendChild(subentry) 

397 size = str(_getVGstats(prefix + val)['physical_size']) 

398 textnode = dom.createTextNode(size) 

399 subentry.appendChild(textnode) 

400 

401 if includeMetadata: 

402 metadataVDI = None 

403 

404 # add SR name_label 

405 mdpath = os.path.join(VG_LOCATION, VG_PREFIX + val) 

406 mdpath = os.path.join(mdpath, MDVOLUME_NAME) 

407 mgtVolActivated = False 

408 try: 

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

410 # probe happens out of band with attach so this volume 

411 # may not have been activated at this point 

412 lvmCache = lvmcache.LVMCache(VG_PREFIX + val) 

413 lvmCache.activateNoRefcount(MDVOLUME_NAME) 

414 mgtVolActivated = True 

415 

416 sr_metadata = \ 

417 srmetadata.LVMMetadataHandler(mdpath, \ 

418 False).getMetadata()[0] 

419 subentry = dom.createElement("name_label") 

420 entry.appendChild(subentry) 

421 textnode = dom.createTextNode(sr_metadata[srmetadata.NAME_LABEL_TAG]) 

422 subentry.appendChild(textnode) 

423 

424 # add SR description 

425 subentry = dom.createElement("name_description") 

426 entry.appendChild(subentry) 

427 textnode = dom.createTextNode(sr_metadata[srmetadata.NAME_DESCRIPTION_TAG]) 

428 subentry.appendChild(textnode) 

429 

430 # add metadata VDI UUID 

431 metadataVDI = srmetadata.LVMMetadataHandler(mdpath, \ 

432 False).findMetadataVDI() 

433 subentry = dom.createElement("pool_metadata_detected") 

434 entry.appendChild(subentry) 

435 if metadataVDI is not None: 

436 subentry.appendChild(dom.createTextNode("true")) 

437 else: 

438 subentry.appendChild(dom.createTextNode("false")) 

439 finally: 

440 if mgtVolActivated: 

441 # deactivate only if we activated it 

442 lvmCache.deactivateNoRefcount(MDVOLUME_NAME) 

443 

444 return dom.toprettyxml() 

445 

446 

447def _openExclusive(dev, retry): 

448 try: 

449 return os.open("%s" % dev, os.O_RDWR | os.O_EXCL) 

450 except OSError as ose: 

451 opened_by = '' 

452 if ose.errno == 16: 

453 if retry: 

454 util.SMlog('Device %s is busy, settle and one shot retry' % 

455 dev) 

456 util.pread2(['/usr/sbin/udevadm', 'settle']) 

457 return _openExclusive(dev, False) 

458 else: 

459 util.SMlog('Device %s is busy after retry' % dev) 

460 

461 util.SMlog('Opening device %s failed with %d' % (dev, ose.errno)) 

462 raise xs_errors.XenError( 

463 'SRInUse', opterr=('Device %s in use, please check your existing ' 

464 + 'SRs for an instance of this device') % dev) 

465 

466 

467def createVG(root, vgname): 

468 systemroot = util.getrootdev() 

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

470 

471 # Create PVs for each device 

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

473 if dev in [systemroot, '%s1' % systemroot, '%s2' % systemroot]: 

474 raise xs_errors.XenError('Rootdev', \ 

475 opterr=('Device %s contains core system files, ' \ 

476 + 'please use another device') % dev) 

477 if not os.path.exists(dev): 

478 raise xs_errors.XenError('InvalidDev', \ 

479 opterr=('Device %s does not exist') % dev) 

480 

481 f = _openExclusive(dev, True) 

482 os.close(f) 

483 

484 # Wipe any fs signature 

485 try: 

486 util.wipefs(dev) 

487 except util.CommandException as inst: 

488 raise xs_errors.XenError('WipefsFailure', opterr='device %s' % dev) # from inst 

489 

490 if not (dev == rootdev): 

491 try: 

492 cmd_lvm([CMD_PVCREATE, "-ff", "-y", "--metadatasize", "10M", dev]) 

493 except util.CommandException as inst: 

494 raise xs_errors.XenError('LVMPartCreate', 

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

496 

497 # Create VG on first device 

498 try: 

499 cmd_lvm([CMD_VGCREATE, "--metadatasize", "10M", vgname, rootdev]) 

500 except: 

501 raise xs_errors.XenError('LVMGroupCreate') 

502 

503 # Then add any additional devs into the VG 

504 for dev in root.split(',')[1:]: 

505 try: 

506 cmd_lvm([CMD_VGEXTEND, vgname, dev]) 

507 except util.CommandException as inst: 

508 # One of the PV args failed, delete SR 

509 try: 

510 cmd_lvm([CMD_VGREMOVE, vgname]) 

511 except: 

512 pass 

513 raise xs_errors.XenError('LVMGroupCreate') 

514 

515 try: 

516 cmd_lvm([CMD_VGCHANGE, "-an", vgname]) 

517 except util.CommandException as inst: 

518 raise xs_errors.XenError('LVMUnMount', opterr='errno is %d' % inst.code) 

519 

520 # End block 

521 

522def getPVsInVG(vgname): 

523 # Get PVs in a specific VG 

524 pvs_ret = cmd_lvm([CMD_PVS, '--separator', ' ', '--noheadings', '-o', 'pv_name,vg_name']) 

525 

526 # Parse each line to extract PV and VG information 

527 # No need to handle exceptions here, return empty list if any error 

528 pvs_in_vg = [] 

529 lines = pvs_ret.strip().split('\n') 

530 for line in lines: 

531 # To avoid invalid return format 

532 parts = line.split() 

533 if len(parts) != 2: 

534 util.SMlog("Warning: Invalid or empty line in pvs output: %s" % line) 

535 continue 

536 pv, vg = parts 

537 if vg == vgname: 

538 pvs_in_vg.append(pv) 

539 

540 util.SMlog("PVs in VG %s: %s" % (vgname, pvs_in_vg)) 

541 return pvs_in_vg 

542 

543def removeVG(root, vgname): 

544 # Check PVs match VG 

545 try: 

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

547 txt = cmd_lvm([CMD_PVS, dev]) 

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

549 raise xs_errors.XenError('LVMNoVolume', \ 

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

551 except util.CommandException as inst: 

552 raise xs_errors.XenError('PVSfailed', \ 

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

554 

555 try: 

556 # Get PVs in VG before removing the VG 

557 devs_in_vg = getPVsInVG(vgname) 

558 cmd_lvm([CMD_VGREMOVE, vgname]) 

559 

560 for dev in devs_in_vg: 

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) 

565 

566 

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

572 

573 

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) 

584 

585 

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

595 

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

597 cmd_lvm(cmd) 

598 

599 

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) 

611 

612 

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) 

620 

621 

622@lvmretry 

623def rename(path, newName): 

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

625 

626 

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) 

633 

634 

635def exists(path): 

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

637 return rc == 0 

638 

639 

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) 

647 

648 

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

655 

656 

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

663 

664 

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 with Fairlock("devicemapper"): 

674 ret = util.pread(cmd) 

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

676 # Restore slave mode lvm.conf 

677 os.environ['LVM_SYSTEM_DIR'] = DEF_LVM_CONF 

678 

679 

680def deactivateNoRefcount(path): 

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

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

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

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

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

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

687 # "lvchange -an" returns success. 

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

689 try: 

690 _deactivate(path) 

691 break 

692 except util.CommandException: 

693 if i >= LVM_FAIL_RETRIES - 1: 

694 raise 

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

696 _lvmBugCleanup(path) 

697 

698 

699@lvmretry 

700def _deactivate(path): 

701 # Records what is using the LVM path in case there is an issue. 

702 # In most cases this should be nothing. 

703 try: 

704 (rc, stdout, stderr) = util.doexec(['/usr/sbin/fuser', "-v", path]) 

705 util.SMlog(f"fuser {path} => {rc} / '{stdout}' / '{stderr}'") 

706 except: 

707 pass 

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

709 

710 

711def _checkActive(path): 

712 if util.pathexists(path): 

713 return True 

714 

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

716 symlinkExists = os.path.lexists(path) 

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

718 

719 mapperDeviceExists = False 

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

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

722 try: 

723 with Fairlock("devicemapper"): 

724 ret = util.pread2(cmd) 

725 mapperDeviceExists = True 

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

727 except util.CommandException: 

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

729 

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

731 mapperPathExists = util.pathexists(mapperPath) 

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

733 (mapperPath, mapperPathExists)) 

734 

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

736 # we can fix this situation manually here 

737 try: 

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

739 os.symlink(mapperPath, path) 

740 except OSError as e: 

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

742 if e.errno != errno.EEXIST: 

743 raise 

744 if util.pathexists(path): 

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

746 return True 

747 

748 return False 

749 

750 

751def _lvmBugCleanup(path): 

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

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

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

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

756 

757 nodeExists = False 

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

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

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

761 

762 try: 

763 with Fairlock("devicemapper"): 

764 util.pread(cmd_st, expect_rc=1) 

765 except util.CommandException as e: 

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

767 nodeExists = True 

768 

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

770 return 

771 

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

773 

774 # destroy the dm device 

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

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

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

778 try: 

779 with Fairlock("devicemapper"): 

780 util.pread2(cmd_rm) 

781 break 

782 except util.CommandException as e: 

783 if i < LVM_FAIL_RETRIES - 1: 

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

785 try: 

786 with Fairlock("devicemapper"): 

787 util.pread(cmd_st, expect_rc=1) 

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

789 " removed".format(mapperDevice) 

790 ) 

791 break 

792 except: 

793 cmd_rm = cmd_rf 

794 time.sleep(1) 

795 else: 

796 # make sure the symlink is still there for consistency 

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

798 os.symlink(mapperPath, path) 

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

800 raise e 

801 

802 if util.pathexists(mapperPath): 

803 os.unlink(mapperPath) 

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

805 

806 # delete the symlink 

807 if os.path.lexists(path): 

808 os.unlink(path) 

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

810 

811 

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

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

814def ensurePathExists(mdpath): 

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

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

817 lvmCache = lvmcache.LVMCache(vgname) 

818 lvmCache.activateNoRefcount(MDVOLUME_NAME) 

819 

820 

821def removeDevMapperEntry(path, strict=True): 

822 try: 

823 # remove devmapper entry using dmsetup 

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

825 cmd_lvm(cmd) 

826 return True 

827 except Exception as e: 

828 if not strict: 

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

830 try: 

831 with Fairlock("devicemapper"): 

832 util.pread(cmd, expect_rc=1) 

833 return True 

834 except: 

835 pass # Continuining will fail and log the right way 

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

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

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

839 return False