Coverage for libs/sm/lvutil.py: 51%

541 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2026-07-10 07:59 +0000

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 

26from sm.core import scsiutil 

27from sm.core import util 

28from sm.core import xs_errors 

29import xml.dom.minidom 

30from sm.lvhdutil import VG_LOCATION, VG_PREFIX 

31from sm.constants import EXT_PREFIX 

32from sm import lvmcache 

33from sm import srmetadata 

34 

35MDVOLUME_NAME = 'MGT' 

36VDI_UUID_TAG_PREFIX = 'vdi_' 

37LVM_BIN = os.path.isfile('/sbin/lvdisplay') and '/sbin' or '/usr/sbin' 

38CMD_VGS = "vgs" 

39CMD_VGCREATE = "vgcreate" 

40CMD_VGREMOVE = "vgremove" 

41CMD_VGCHANGE = "vgchange" 

42CMD_VGEXTEND = "vgextend" 

43CMD_PVS = "pvs" 

44CMD_PVCREATE = "pvcreate" 

45CMD_PVREMOVE = "pvremove" 

46CMD_PVRESIZE = "pvresize" 

47CMD_LVS = "lvs" 

48CMD_LVDISPLAY = "lvdisplay" 

49CMD_LVCREATE = "lvcreate" 

50CMD_LVREMOVE = "lvremove" 

51CMD_LVCHANGE = "lvchange" 

52CMD_LVRENAME = "lvrename" 

53CMD_LVRESIZE = "lvresize" 

54CMD_LVEXTEND = "lvextend" 

55CMD_DMSETUP = "/sbin/dmsetup" 

56 

57MAX_OPERATION_DURATION = 15 

58 

59LVM_SIZE_INCREMENT = 4 * 1024 * 1024 

60LV_TAG_HIDDEN = "hidden" 

61LVM_FAIL_RETRIES = 10 

62 

63MASTER_LVM_CONF = '/etc/lvm/master' 

64DEF_LVM_CONF = '/etc/lvm' 

65 

66VG_COMMANDS = frozenset({CMD_VGS, CMD_VGCREATE, CMD_VGREMOVE, CMD_VGCHANGE, 

67 CMD_VGEXTEND}) 

68PV_COMMANDS = frozenset({CMD_PVS, CMD_PVCREATE, CMD_PVREMOVE, CMD_PVRESIZE}) 

69LV_COMMANDS = frozenset({CMD_LVS, CMD_LVDISPLAY, CMD_LVCREATE, CMD_LVREMOVE, 

70 CMD_LVCHANGE, CMD_LVRENAME, CMD_LVRESIZE, 

71 CMD_LVEXTEND}) 

72DM_COMMANDS = frozenset({CMD_DMSETUP}) 

73 

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

75 

76LVM_LOCK = 'lvm' 

77 

78 

79def extract_vgname(str_in): 

80 """Search for and return a VG name 

81 

82 Search 'str_in' for a substring in the form of 'VG_XenStorage-<UUID>'.  

83 If there are more than one VG names, the first is returned. 

84 

85 Input: 

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

87 in the format specified above. 

88 

89 Return: 

90 vgname -- if found -> (str) 

91 if not found -> None 

92 

93 Raise: 

94 TypeError 

95 """ 

96 

97 if not util.is_string(str_in): 

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

99 

100 i = str_in.find(VG_PREFIX) 

101 prefix = VG_PREFIX 

102 

103 if i == -1: 

104 i = str_in.find(EXT_PREFIX) 

105 prefix = EXT_PREFIX 

106 

107 uuid_start = i + len(prefix) 

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

109 

110 if i != -1 and re_obj: 

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

112 

113 return None 

114 

115LVM_RETRY_ERRORS = [ 

116 "Incorrect checksum in metadata area header" 

117] 

118 

119 

120def lvmretry(func): 

121 def check_exception(exception): 

122 retry = False 

123 for error in LVM_RETRY_ERRORS: 

124 if error in exception.reason: 

125 retry = True 

126 return retry 

127 

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

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

130 try: 

131 return func(*args, **kwargs) 

132 except util.CommandException as ce: 

133 retry = check_exception(ce) 

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

135 raise 

136 

137 time.sleep(1) 

138 

139 decorated.__name__ = func.__name__ 

140 return decorated 

141 

142 

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

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

145 

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

147 

148 Input: 

149 cmd -- (list) lvm command 

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

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

152 

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

154 to execute the lvm command 

155 Default: util.pread2() 

156 

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

158 to 'pread_func' 

159 

160 Return: 

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

162 

163 Raise: 

164 util.CommandException 

165 """ 

166 

167 if type(cmd) is not list: 

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

169 return None 

170 if not len(cmd): 

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

172 return None 

173 

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

175 

176 if lvm_cmd not in LVM_COMMANDS: 

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

178 return None 

179 

180 for arg in lvm_args: 

181 if not util.is_string(arg): 

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

183 return None 

184 

185 with Fairlock("devicemapper"): 

186 start_time = time.time() 

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

188 end_time = time.time() 

189 

190 if (end_time - start_time > MAX_OPERATION_DURATION): 

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

192 

193 return stdout 

194 

195 

196class LVInfo: 

197 name = "" 

198 size = 0 

199 active = False 

200 open = False 

201 hidden = False 

202 readonly = False 

203 

204 def __init__(self, name): 

205 self.name = name 

206 

207 def toString(self): 

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

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

210 self.readonly) 

211 

212 

213def _checkVG(vgname): 

214 try: 

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

216 return True 

217 except: 

218 return False 

219 

220 

221def _checkPV(pvname): 

222 try: 

223 cmd_lvm([CMD_PVS, pvname]) 

224 return True 

225 except: 

226 return False 

227 

228 

229def _checkLV(path): 

230 try: 

231 cmd_lvm([CMD_LVDISPLAY, path]) 

232 return True 

233 except: 

234 return False 

235 

236 

237def _getLVsize(path): 

238 try: 

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

240 return int(lines[6]) * 512 

241 except: 

242 raise xs_errors.XenError('VDIUnavailable', \ 

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

244 

245 

246def _getVGstats(vgname): 

247 try: 

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

249 "--units", "b", vgname], 

250 pread_func=util.pread).split() 

251 size = int(text[5]) 

252 freespace = int(text[6]) 

253 utilisation = size - freespace 

254 stats = {} 

255 stats['physical_size'] = size 

256 stats['physical_utilisation'] = utilisation 

257 stats['freespace'] = freespace 

258 return stats 

259 except util.CommandException as inst: 

260 raise xs_errors.XenError('VDILoad', \ 

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

262 except ValueError: 

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

264 

265 

266def _getPVstats(dev): 

267 try: 

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

269 "--units", "b", dev], 

270 pread_func=util.pread).split() 

271 size = int(text[4]) 

272 freespace = int(text[5]) 

273 utilisation = size - freespace 

274 stats = {} 

275 stats['physical_size'] = size 

276 stats['physical_utilisation'] = utilisation 

277 stats['freespace'] = freespace 

278 return stats 

279 except util.CommandException as inst: 

280 raise xs_errors.XenError('VDILoad', \ 

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

282 except ValueError: 

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

284 

285 

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

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

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

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

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

291# returned. 

292# E.g. 

293# PV VG Fmt Attr PSize PFree 

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

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

296def _get_sr_uuid(pvname, prefix_list): 

297 try: 

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

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

300 except: 

301 return "" 

302 

303 

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

305# Volume Group 

306# e.g. 

307# PV VG Fmt Attr PSize PFree 

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

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

310def get_pv_for_vg(vgname): 

311 try: 

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

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

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

315 except util.CommandException: 

316 return [] 

317 

318 

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

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

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

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

323# 

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

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

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

327# specific. 

328def match_VG(s, prefix_list): 

329 for val in prefix_list: 

330 regex = re.compile(val) 

331 if regex.search(s, 0): 

332 return s.split(val)[1] 

333 return "" 

334 

335 

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

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

338# devices. Exceptions are ignored. 

339def scan_srlist(prefix, root): 

340 VGs = {} 

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

342 try: 

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

344 if len(sr_uuid): 

345 if sr_uuid in VGs: 

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

347 else: 

348 VGs[sr_uuid] = dev 

349 except Exception as e: 

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

351 continue 

352 return VGs 

353 

354 

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

356# <SRlist> 

357# <SR> 

358# <UUID>...</UUID> 

359# <Devlist>...</Devlist> 

360# <size>...</size> 

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

362# are supplied. --> 

363# <name_label>...</name_label> 

364# <name_description>...</name_description> 

365# <pool_metadata_detected>...</pool_metadata_detected> 

366# </SR> 

367# 

368# <SR>...</SR> 

369# </SRlist> 

370# 

371# Arguments: 

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

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

374# includeMetadata (optional): include additional information 

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

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

377 element = dom.createElement("SRlist") 

378 dom.appendChild(element) 

379 

380 for val in VGs: 

381 entry = dom.createElement('SR') 

382 element.appendChild(entry) 

383 

384 subentry = dom.createElement("UUID") 

385 entry.appendChild(subentry) 

386 textnode = dom.createTextNode(val) 

387 subentry.appendChild(textnode) 

388 

389 subentry = dom.createElement("Devlist") 

390 entry.appendChild(subentry) 

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

392 subentry.appendChild(textnode) 

393 

394 subentry = dom.createElement("size") 

395 entry.appendChild(subentry) 

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

397 textnode = dom.createTextNode(size) 

398 subentry.appendChild(textnode) 

399 

400 if includeMetadata: 

401 metadataVDI = None 

402 

403 # add SR name_label 

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

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

406 mgtVolActivated = False 

407 try: 

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

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

410 # may not have been activated at this point 

411 lvmCache = lvmcache.LVMCache(VG_PREFIX + val) 

412 lvmCache.activateNoRefcount(MDVOLUME_NAME) 

413 mgtVolActivated = True 

414 

415 sr_metadata = \ 

416 srmetadata.LVMMetadataHandler(mdpath, \ 

417 False).getMetadata()[0] 

418 subentry = dom.createElement("name_label") 

419 entry.appendChild(subentry) 

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

421 subentry.appendChild(textnode) 

422 

423 # add SR description 

424 subentry = dom.createElement("name_description") 

425 entry.appendChild(subentry) 

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

427 subentry.appendChild(textnode) 

428 

429 # add metadata VDI UUID 

430 metadataVDI = srmetadata.LVMMetadataHandler(mdpath, \ 

431 False).findMetadataVDI() 

432 subentry = dom.createElement("pool_metadata_detected") 

433 entry.appendChild(subentry) 

434 if metadataVDI is not None: 

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

436 else: 

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

438 finally: 

439 if mgtVolActivated: 

440 # deactivate only if we activated it 

441 lvmCache.deactivateNoRefcount(MDVOLUME_NAME) 

442 

443 return dom.toprettyxml() 

444 

445 

446def _openExclusive(dev, retry): 

447 try: 

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

449 except OSError as ose: 

450 opened_by = '' 

451 if ose.errno == 16: 

452 if retry: 

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

454 dev) 

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

456 return _openExclusive(dev, False) 

457 else: 

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

459 

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

461 raise xs_errors.XenError( 

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

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

464 

465 

466def createVG(root, vgname): 

467 systemroot = util.getrootdev() 

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

469 

470 # Create PVs for each device 

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

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

473 raise xs_errors.XenError('Rootdev', \ 

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

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

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

477 raise xs_errors.XenError('InvalidDev', \ 

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

479 

480 f = _openExclusive(dev, True) 

481 os.close(f) 

482 try: 

483 # Overwrite the disk header, try direct IO first 

484 cmd = [util.CMD_DD, "if=/dev/zero", "of=%s" % dev, "bs=1M", 

485 "count=10", "oflag=direct"] 

486 util.pread2(cmd) 

487 except util.CommandException as inst: 

488 if inst.code == errno.EPERM: 

489 try: 

490 # Overwrite the disk header, try normal IO 

491 cmd = [util.CMD_DD, "if=/dev/zero", "of=%s" % dev, 

492 "bs=1M", "count=10"] 

493 util.pread2(cmd) 

494 except util.CommandException as inst: 

495 raise xs_errors.XenError('LVMWrite', \ 

496 opterr='device %s' % dev) 

497 else: 

498 raise xs_errors.XenError('LVMWrite', \ 

499 opterr='device %s' % dev) 

500 

501 if not (dev == rootdev): 

502 try: 

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

504 except util.CommandException as inst: 

505 raise xs_errors.XenError('LVMPartCreate', 

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

507 

508 # Create VG on first device 

509 try: 

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

511 except: 

512 raise xs_errors.XenError('LVMGroupCreate') 

513 

514 # Then add any additional devs into the VG 

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

516 try: 

517 cmd_lvm([CMD_VGEXTEND, vgname, dev]) 

518 except util.CommandException as inst: 

519 # One of the PV args failed, delete SR 

520 try: 

521 cmd_lvm([CMD_VGREMOVE, vgname]) 

522 except: 

523 pass 

524 raise xs_errors.XenError('LVMGroupCreate') 

525 

526 try: 

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

528 except util.CommandException as inst: 

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

530 

531 # End block 

532 

533def getPVsInVG(vgname): 

534 # Get PVs in a specific VG, returns PV uuids 

535 pvs_ret = cmd_lvm([CMD_PVS, '--noheadings', '-o', 'pv_uuid,vg_name']) 

536 

537 # Parse each line to extract PV and VG information 

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

539 pvs_in_vg = [] 

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

541 for line in lines: 

542 # To avoid invalid return format 

543 parts = line.split() 

544 if len(parts) != 2: 

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

546 continue 

547 pv_uuid, vg = parts 

548 if vg == vgname: 

549 pvs_in_vg.append(pv_uuid) 

550 

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

552 return pvs_in_vg 

553 

554def getPVsWithUUID(pv_uuid): 

555 # Get PVs with a specific UUID, returns PV device names 

556 pvs_ret = cmd_lvm([CMD_PVS, '--noheadings', '-o', 'pv_name,pv_uuid']) 

557 

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

559 pvs_in_vg = [] 

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

561 for line in lines: 

562 # To avoid invalid return format 

563 parts = line.split() 

564 if len(parts) != 2: 

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

566 continue 

567 pv, uuid = parts 

568 if uuid == pv_uuid: 

569 pvs_in_vg.append(pv) 

570 

571 util.SMlog("PVs with uuid %s: %s" % (pv_uuid, pvs_in_vg)) 

572 return pvs_in_vg 

573 

574def removeVG(root, vgname): 

575 # Check PVs match VG 

576 try: 

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

578 txt = cmd_lvm([CMD_PVS, dev]) 

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

580 raise xs_errors.XenError('LVMNoVolume', \ 

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

582 except util.CommandException as inst: 

583 raise xs_errors.XenError('PVSfailed', \ 

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

585 

586 try: 

587 # Get PVs(uuid) in VG before removing the VG 

588 pv_uuids_in_vg = getPVsInVG(vgname) 

589 cmd_lvm([CMD_VGREMOVE, vgname]) 

590 

591 # For multipath, after vgremove, LVM reassign the underlying device node represents the PV. 

592 # To remove the correct PV, use uuid to get the real device that represents the PV. 

593 for pv_uuid in pv_uuids_in_vg: 

594 pvs = getPVsWithUUID(pv_uuid) 

595 for pv in pvs: 

596 cmd_lvm([CMD_PVREMOVE, pv]) 

597 except util.CommandException as inst: 

598 raise xs_errors.XenError('LVMDelete', \ 

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

600 

601 

602def resizePV(dev): 

603 try: 

604 cmd_lvm([CMD_PVRESIZE, dev]) 

605 except util.CommandException as inst: 

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

607 

608 

609def setActiveVG(path, active): 

610 "activate or deactivate VG 'path'" 

611 val = "n" 

612 if active: 

613 val = "y" 

614 text = cmd_lvm([CMD_VGCHANGE, "-a" + val, path]) 

615 

616 

617def checkPVScsiIds(vgname, SCSIid): 

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

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

620 text = cmd_lvm(cmd) 

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

622 for pv_path in pv_paths: 

623 pv_scsi_id = scsiutil.getSCSIid(pv_path) 

624 if pv_scsi_id != SCSIid: 

625 raise xs_errors.XenError( 

626 'PVMultiIDs', 

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

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

629 

630@lvmretry 

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

632 if size_in_percentage: 

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

634 else: 

635 size_mb = size // (1024 * 1024) 

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

637 if tag: 

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

639 

640 cmd.extend(['-W', 'y', '--yes']) 

641 cmd_lvm(cmd) 

642 

643 

644def remove(path, config_param=None): 

645 # see deactivateNoRefcount() 

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

647 try: 

648 _remove(path, config_param) 

649 break 

650 except util.CommandException as e: 

651 if i >= LVM_FAIL_RETRIES - 1: 

652 raise 

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

654 _lvmBugCleanup(path) 

655 

656 

657@lvmretry 

658def _remove(path, config_param=None): 

659 CONFIG_TAG = "--config" 

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

661 if config_param: 

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

663 ret = cmd_lvm(cmd) 

664 

665 

666@lvmretry 

667def rename(path, newName): 

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

669 

670 

671@lvmretry 

672def setReadonly(path, readonly): 

673 val = "r" 

674 if not readonly: 

675 val += "w" 

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

677 

678 

679def exists(path): 

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

681 return rc == 0 

682 

683 

684@lvmretry 

685def setSize(path, size, confirm): 

686 sizeMB = size // (1024 * 1024) 

687 if confirm: 

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

689 else: 

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

691 

692 

693@lvmretry 

694def setHidden(path, hidden=True): 

695 opt = "--addtag" 

696 if not hidden: 

697 opt = "--deltag" 

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

699 

700 

701@lvmretry 

702def _activate(path): 

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

704 cmd_lvm(cmd) 

705 if not _checkActive(path): 

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

707 

708 

709def activateNoRefcount(path, refresh): 

710 _activate(path) 

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

712 # Override slave mode lvm.conf for this command 

713 os.environ['LVM_SYSTEM_DIR'] = MASTER_LVM_CONF 

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

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

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

717 with Fairlock("devicemapper"): 

718 ret = util.pread(cmd) 

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

720 # Restore slave mode lvm.conf 

721 os.environ['LVM_SYSTEM_DIR'] = DEF_LVM_CONF 

722 

723 

724def deactivateNoRefcount(path): 

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

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

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

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

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

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

731 # "lvchange -an" returns success. 

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

733 try: 

734 _deactivate(path) 

735 break 

736 except util.CommandException: 

737 if i >= LVM_FAIL_RETRIES - 1: 

738 raise 

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

740 _lvmBugCleanup(path) 

741 

742 

743@lvmretry 

744def _deactivate(path): 

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

746 

747 

748def _checkActive(path): 

749 if util.pathexists(path): 

750 return True 

751 

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

753 symlinkExists = os.path.lexists(path) 

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

755 

756 mapperDeviceExists = False 

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

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

759 try: 

760 with Fairlock("devicemapper"): 

761 ret = util.pread2(cmd) 

762 mapperDeviceExists = True 

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

764 except util.CommandException: 

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

766 

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

768 mapperPathExists = util.pathexists(mapperPath) 

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

770 (mapperPath, mapperPathExists)) 

771 

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

773 # we can fix this situation manually here 

774 try: 

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

776 os.symlink(mapperPath, path) 

777 except OSError as e: 

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

779 if e.errno != errno.EEXIST: 

780 raise 

781 if util.pathexists(path): 

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

783 return True 

784 

785 return False 

786 

787 

788def _lvmBugCleanup(path): 

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

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

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

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

793 

794 nodeExists = False 

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

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

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

798 

799 try: 

800 with Fairlock("devicemapper"): 

801 util.pread(cmd_st, expect_rc=1) 

802 except util.CommandException as e: 

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

804 nodeExists = True 

805 

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

807 return 

808 

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

810 

811 # destroy the dm device 

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

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

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

815 try: 

816 with Fairlock("devicemapper"): 

817 util.pread2(cmd_rm) 

818 break 

819 except util.CommandException as e: 

820 if i < LVM_FAIL_RETRIES - 1: 

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

822 try: 

823 with Fairlock("devicemapper"): 

824 util.pread(cmd_st, expect_rc=1) 

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

826 " removed".format(mapperDevice) 

827 ) 

828 break 

829 except: 

830 cmd_rm = cmd_rf 

831 time.sleep(1) 

832 else: 

833 # make sure the symlink is still there for consistency 

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

835 os.symlink(mapperPath, path) 

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

837 raise e 

838 

839 if util.pathexists(mapperPath): 

840 os.unlink(mapperPath) 

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

842 

843 # delete the symlink 

844 if os.path.lexists(path): 

845 os.unlink(path) 

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

847 

848 

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

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

851def ensurePathExists(mdpath): 

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

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

854 lvmCache = lvmcache.LVMCache(vgname) 

855 lvmCache.activateNoRefcount(MDVOLUME_NAME) 

856 

857 

858def removeDevMapperEntry(path, strict=True): 

859 try: 

860 # remove devmapper entry using dmsetup 

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

862 cmd_lvm(cmd) 

863 return True 

864 except Exception as e: 

865 if not strict: 

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

867 try: 

868 with Fairlock("devicemapper"): 

869 util.pread(cmd, expect_rc=1) 

870 return True 

871 except: 

872 pass # Continuining will fail and log the right way 

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

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

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

876 return False