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 

25import scsiutil 

26from fairlock import Fairlock 

27import util 

28import xs_errors 

29import xml.dom.minidom 

30from constants import EXT_PREFIX, VG_LOCATION, VG_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 

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

119 return util.roundup(LVM_SIZE_INCREMENT, size) 

120 

121 

122def lvmretry(func): 

123 def check_exception(exception): 

124 retry = False 

125 for error in LVM_RETRY_ERRORS: 

126 if error in exception.reason: 

127 retry = True 

128 return retry 

129 

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

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

132 try: 

133 return func(*args, **kwargs) 

134 except util.CommandException as ce: 

135 retry = check_exception(ce) 

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

137 raise 

138 

139 time.sleep(1) 

140 

141 decorated.__name__ = func.__name__ 

142 return decorated 

143 

144 

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

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

147 

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

149 

150 Input: 

151 cmd -- (list) lvm command 

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

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

154 

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

156 to execute the lvm command 

157 Default: util.pread2() 

158 

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

160 to 'pread_func' 

161 

162 Return: 

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

164 

165 Raise: 

166 util.CommandException 

167 """ 

168 

169 if type(cmd) is not list: 

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

171 return None 

172 if not len(cmd): 

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

174 return None 

175 

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

177 

178 if lvm_cmd not in LVM_COMMANDS: 

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

180 return None 

181 

182 for arg in lvm_args: 

183 if not util.is_string(arg): 

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

185 return None 

186 

187 with Fairlock("devicemapper"): 

188 start_time = time.time() 

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

190 end_time = time.time() 

191 

192 if (end_time - start_time > MAX_OPERATION_DURATION): 

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

194 

195 return stdout 

196 

197 

198class LVInfo: 

199 name = "" 

200 size = 0 

201 active = False 

202 open = False 

203 hidden = False 

204 readonly = False 

205 

206 def __init__(self, name): 

207 self.name = name 

208 

209 def toString(self): 

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

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

212 self.readonly) 

213 

214 

215def _checkVG(vgname): 

216 try: 

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

218 return True 

219 except: 

220 return False 

221 

222 

223def _checkPV(pvname): 

224 try: 

225 cmd_lvm([CMD_PVS, pvname]) 

226 return True 

227 except: 

228 return False 

229 

230 

231def _checkLV(path): 

232 try: 

233 cmd_lvm([CMD_LVDISPLAY, path]) 

234 return True 

235 except: 

236 return False 

237 

238 

239def _getLVsize(path): 

240 try: 

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

242 return int(lines[6]) * 512 

243 except: 

244 raise xs_errors.XenError('VDIUnavailable', \ 

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

246 

247 

248def _getVGstats(vgname): 

249 try: 

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

251 "--units", "b", vgname], 

252 pread_func=util.pread).split() 

253 size = int(text[5]) 

254 freespace = int(text[6]) 

255 utilisation = size - freespace 

256 stats = {} 

257 stats['physical_size'] = size 

258 stats['physical_utilisation'] = utilisation 

259 stats['freespace'] = freespace 

260 return stats 

261 except util.CommandException as inst: 

262 raise xs_errors.XenError('VDILoad', \ 

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

264 except ValueError: 

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

266 

267 

268def _getPVstats(dev): 

269 try: 

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

271 "--units", "b", dev], 

272 pread_func=util.pread).split() 

273 size = int(text[4]) 

274 freespace = int(text[5]) 

275 utilisation = size - freespace 

276 stats = {} 

277 stats['physical_size'] = size 

278 stats['physical_utilisation'] = utilisation 

279 stats['freespace'] = freespace 

280 return stats 

281 except util.CommandException as inst: 

282 raise xs_errors.XenError('VDILoad', \ 

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

284 except ValueError: 

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

286 

287 

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

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

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

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

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

293# returned. 

294# E.g. 

295# PV VG Fmt Attr PSize PFree 

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

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

298def _get_sr_uuid(pvname, prefix_list): 

299 try: 

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

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

302 except: 

303 return "" 

304 

305 

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

307# Volume Group 

308# e.g. 

309# PV VG Fmt Attr PSize PFree 

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

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

312def get_pv_for_vg(vgname): 

313 try: 

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

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

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

317 except util.CommandException: 

318 return [] 

319 

320 

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

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

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

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

325# 

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

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

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

329# specific. 

330def match_VG(s, prefix_list): 

331 for val in prefix_list: 

332 regex = re.compile(val) 

333 if regex.search(s, 0): 

334 return s.split(val)[1] 

335 return "" 

336 

337 

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

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

340# devices. Exceptions are ignored. 

341def scan_srlist(prefix, root): 

342 VGs = {} 

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

344 try: 

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

346 if len(sr_uuid): 

347 if sr_uuid in VGs: 

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

349 else: 

350 VGs[sr_uuid] = dev 

351 except Exception as e: 

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

353 continue 

354 return VGs 

355 

356 

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

358# <SRlist> 

359# <SR> 

360# <UUID>...</UUID> 

361# <Devlist>...</Devlist> 

362# <size>...</size> 

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

364# are supplied. --> 

365# <name_label>...</name_label> 

366# <name_description>...</name_description> 

367# <pool_metadata_detected>...</pool_metadata_detected> 

368# </SR> 

369# 

370# <SR>...</SR> 

371# </SRlist> 

372# 

373# Arguments: 

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

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

376# includeMetadata (optional): include additional information 

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

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

379 element = dom.createElement("SRlist") 

380 dom.appendChild(element) 

381 

382 for val in VGs: 

383 entry = dom.createElement('SR') 

384 element.appendChild(entry) 

385 

386 subentry = dom.createElement("UUID") 

387 entry.appendChild(subentry) 

388 textnode = dom.createTextNode(val) 

389 subentry.appendChild(textnode) 

390 

391 subentry = dom.createElement("Devlist") 

392 entry.appendChild(subentry) 

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

394 subentry.appendChild(textnode) 

395 

396 subentry = dom.createElement("size") 

397 entry.appendChild(subentry) 

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

399 textnode = dom.createTextNode(size) 

400 subentry.appendChild(textnode) 

401 

402 if includeMetadata: 

403 metadataVDI = None 

404 

405 # add SR name_label 

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

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

408 mgtVolActivated = False 

409 try: 

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

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

412 # may not have been activated at this point 

413 lvmCache = lvmcache.LVMCache(VG_PREFIX + val) 

414 lvmCache.activateNoRefcount(MDVOLUME_NAME) 

415 mgtVolActivated = True 

416 

417 sr_metadata = \ 

418 srmetadata.LVMMetadataHandler(mdpath, \ 

419 False).getMetadata()[0] 

420 subentry = dom.createElement("name_label") 

421 entry.appendChild(subentry) 

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

423 subentry.appendChild(textnode) 

424 

425 # add SR description 

426 subentry = dom.createElement("name_description") 

427 entry.appendChild(subentry) 

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

429 subentry.appendChild(textnode) 

430 

431 # add metadata VDI UUID 

432 metadataVDI = srmetadata.LVMMetadataHandler(mdpath, \ 

433 False).findMetadataVDI() 

434 subentry = dom.createElement("pool_metadata_detected") 

435 entry.appendChild(subentry) 

436 if metadataVDI is not None: 

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

438 else: 

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

440 finally: 

441 if mgtVolActivated: 

442 # deactivate only if we activated it 

443 lvmCache.deactivateNoRefcount(MDVOLUME_NAME) 

444 

445 return dom.toprettyxml() 

446 

447 

448def _openExclusive(dev, retry): 

449 try: 

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

451 except OSError as ose: 

452 opened_by = '' 

453 if ose.errno == 16: 

454 if retry: 

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

456 dev) 

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

458 return _openExclusive(dev, False) 

459 else: 

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

461 

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

463 raise xs_errors.XenError( 

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

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

466 

467 

468def createVG(root, vgname): 

469 systemroot = util.getrootdev() 

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

471 

472 # Create PVs for each device 

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

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

475 raise xs_errors.XenError('Rootdev', \ 

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

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

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

479 raise xs_errors.XenError('InvalidDev', \ 

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

481 

482 f = _openExclusive(dev, True) 

483 os.close(f) 

484 

485 # Wipe any fs signature 

486 try: 

487 util.wipefs(dev) 

488 except util.CommandException as inst: 

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

490 

491 if not (dev == rootdev): 

492 try: 

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

494 except util.CommandException as inst: 

495 raise xs_errors.XenError('LVMPartCreate', 

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

497 

498 # Create VG on first device 

499 try: 

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

501 except: 

502 raise xs_errors.XenError('LVMGroupCreate') 

503 

504 # Then add any additional devs into the VG 

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

506 try: 

507 cmd_lvm([CMD_VGEXTEND, vgname, dev]) 

508 except util.CommandException as inst: 

509 # One of the PV args failed, delete SR 

510 try: 

511 cmd_lvm([CMD_VGREMOVE, vgname]) 

512 except: 

513 pass 

514 raise xs_errors.XenError('LVMGroupCreate') 

515 

516 try: 

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

518 except util.CommandException as inst: 

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

520 

521 # End block 

522 

523def getPVsInVG(vgname): 

524 # Get PVs in a specific VG 

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

526 

527 # Parse each line to extract PV and VG information 

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

529 pvs_in_vg = [] 

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

531 for line in lines: 

532 # To avoid invalid return format 

533 parts = line.split() 

534 if len(parts) != 2: 

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

536 continue 

537 pv, vg = parts 

538 if vg == vgname: 

539 pvs_in_vg.append(pv) 

540 

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

542 return pvs_in_vg 

543 

544def removeVG(root, vgname): 

545 # Check PVs match VG 

546 try: 

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

548 txt = cmd_lvm([CMD_PVS, dev]) 

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

550 raise xs_errors.XenError('LVMNoVolume', \ 

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

552 except util.CommandException as inst: 

553 raise xs_errors.XenError('PVSfailed', \ 

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

555 

556 try: 

557 # Get PVs in VG before removing the VG 

558 devs_in_vg = getPVsInVG(vgname) 

559 cmd_lvm([CMD_VGREMOVE, vgname]) 

560 

561 for dev in devs_in_vg: 

562 cmd_lvm([CMD_PVREMOVE, dev]) 

563 except util.CommandException as inst: 

564 raise xs_errors.XenError('LVMDelete', \ 

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

566 

567 

568def resizePV(dev): 

569 try: 

570 cmd_lvm([CMD_PVRESIZE, dev]) 

571 except util.CommandException as inst: 

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

573 

574 

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

576 "activate or deactivate VG 'path'" 

577 val = "n" 

578 if active: 

579 val = "y" 

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

581 if config: 

582 cmd.append("--config") 

583 cmd.append(config) 

584 cmd_lvm(cmd) 

585 

586 

587def checkPVScsiIds(vgname, SCSIid): 

588 # Get all the PVs for the specified vgName even if not active 

589 cmd = [CMD_PVS, '-a', '--select', f'vgname={vgname}', '--no-headings'] 

590 text = cmd_lvm(cmd) 

591 pv_paths = [x.split()[0] for x in text.splitlines()] 

592 for pv_path in pv_paths: 

593 pv_scsi_id = scsiutil.getSCSIid(pv_path) 

594 if pv_scsi_id != SCSIid: 

595 raise xs_errors.XenError( 

596 'PVMultiIDs', 

597 opterr=f'Found PVs {",".join(pv_paths)} and unexpected ' 

598 f'SCSI ID {pv_scsi_id}, expected {SCSIid}') 

599 

600@lvmretry 

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

602 if size_in_percentage: 

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

604 else: 

605 size_mb = size // (1024 * 1024) 

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

607 if tag: 

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

609 

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

611 cmd_lvm(cmd) 

612 

613 

614def remove(path, config_param=None): 

615 # see deactivateNoRefcount() 

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

617 try: 

618 _remove(path, config_param) 

619 break 

620 except util.CommandException as e: 

621 if i >= LVM_FAIL_RETRIES - 1: 

622 raise 

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

624 _lvmBugCleanup(path) 

625 

626 

627@lvmretry 

628def _remove(path, config_param=None): 

629 CONFIG_TAG = "--config" 

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

631 if config_param: 

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

633 ret = cmd_lvm(cmd) 

634 

635 

636@lvmretry 

637def rename(path, newName): 

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

639 

640 

641@lvmretry 

642def setReadonly(path, readonly): 

643 val = "r" 

644 if not readonly: 

645 val += "w" 

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

647 

648 

649def exists(path): 

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

651 return rc == 0 

652 

653def listLv(path=None): 

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

655 if rc == 0: 655 ↛ 658line 655 didn't jump to line 658, because the condition on line 655 was never false

656 return stdout 

657 

658 raise xs_errors.XenError('VolNotFound') 

659 

660@lvmretry 

661def setSize(path, size, confirm): 

662 sizeMB = size // (1024 * 1024) 

663 if confirm: 

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

665 else: 

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

667 

668 

669@lvmretry 

670def setHidden(path, hidden=True): 

671 opt = "--addtag" 

672 if not hidden: 

673 opt = "--deltag" 

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

675 

676 

677@lvmretry 

678def _activate(path): 

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

680 cmd_lvm(cmd) 

681 if not _checkActive(path): 

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

683 

684 

685def activateNoRefcount(path, refresh): 

686 _activate(path) 

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

688 # Override slave mode lvm.conf for this command 

689 os.environ['LVM_SYSTEM_DIR'] = MASTER_LVM_CONF 

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

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

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

693 with Fairlock("devicemapper"): 

694 ret = util.pread(cmd) 

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

696 # Restore slave mode lvm.conf 

697 os.environ['LVM_SYSTEM_DIR'] = DEF_LVM_CONF 

698 

699 

700def deactivateNoRefcount(path): 

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

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

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

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

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

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

707 # "lvchange -an" returns success. 

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

709 try: 

710 _deactivate(path) 

711 break 

712 except util.CommandException: 

713 if i >= LVM_FAIL_RETRIES - 1: 

714 raise 

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

716 _lvmBugCleanup(path) 

717 

718 

719@lvmretry 

720def _deactivate(path): 

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

722 

723 

724def _checkActive(path): 

725 if util.pathexists(path): 

726 return True 

727 

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

729 symlinkExists = os.path.lexists(path) 

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

731 

732 util.SMlog(f"LVM says {listLv(path=path)}") 

733 

734 mapperDeviceExists = False 

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

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

737 try: 

738 with Fairlock("devicemapper"): 

739 ret = util.pread2(cmd) 

740 mapperDeviceExists = True 

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

742 except util.CommandException: 

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

744 

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

746 mapperPathExists = util.pathexists(mapperPath) 

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

748 (mapperPath, mapperPathExists)) 

749 

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

751 # we can fix this situation manually here 

752 try: 

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

754 os.symlink(mapperPath, path) 

755 except OSError as e: 

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

757 if e.errno != errno.EEXIST: 

758 raise 

759 if util.pathexists(path): 

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

761 return True 

762 

763 return False 

764 

765 

766def _lvmBugCleanup(path): 

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

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

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

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

771 

772 nodeExists = False 

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

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

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

776 

777 try: 

778 with Fairlock("devicemapper"): 

779 util.pread(cmd_st, expect_rc=1) 

780 except util.CommandException as e: 

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

782 nodeExists = True 

783 

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

785 return 

786 

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

788 

789 # destroy the dm device 

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

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

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

793 try: 

794 with Fairlock("devicemapper"): 

795 util.pread2(cmd_rm) 

796 break 

797 except util.CommandException as e: 

798 if i < LVM_FAIL_RETRIES - 1: 

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

800 try: 

801 with Fairlock("devicemapper"): 

802 util.pread(cmd_st, expect_rc=1) 

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

804 " removed".format(mapperDevice) 

805 ) 

806 break 

807 except: 

808 cmd_rm = cmd_rf 

809 time.sleep(1) 

810 else: 

811 # make sure the symlink is still there for consistency 

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

813 os.symlink(mapperPath, path) 

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

815 raise e 

816 

817 if util.pathexists(mapperPath): 

818 os.unlink(mapperPath) 

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

820 

821 # delete the symlink 

822 if os.path.lexists(path): 

823 os.unlink(path) 

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

825 

826 

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

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

829def ensurePathExists(mdpath): 

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

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

832 lvmCache = lvmcache.LVMCache(vgname) 

833 lvmCache.activateNoRefcount(MDVOLUME_NAME) 

834 

835 

836def removeDevMapperEntry(path, strict=True): 

837 try: 

838 # remove devmapper entry using dmsetup 

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

840 cmd_lvm(cmd) 

841 return True 

842 except Exception as e: 

843 if not strict: 

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

845 try: 

846 with Fairlock("devicemapper"): 

847 util.pread(cmd, expect_rc=1) 

848 return True 

849 except: 

850 pass # Continuining will fail and log the right way 

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

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

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

854 return False