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#!/usr/bin/python3 

2# 

3# Copyright (C) Citrix Systems Inc. 

4# 

5# This program is free software; you can redistribute it and/or modify 

6# it under the terms of the GNU Lesser General Public License as published 

7# by the Free Software Foundation; version 2.1 only. 

8# 

9# This program is distributed in the hope that it will be useful, 

10# but WITHOUT ANY WARRANTY; without even the implied warranty of 

11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

12# GNU Lesser General Public License for more details. 

13# 

14# You should have received a copy of the GNU Lesser General Public License 

15# along with this program; if not, write to the Free Software Foundation, Inc., 

16# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 

17# 

18# LVMSR: VHD and QCOW2 on LVM storage repository 

19# 

20 

21from sm_typing import Dict, List, override 

22 

23import SR 

24from SR import deviceCheck 

25import VDI 

26import SRCommand 

27import util 

28import lvutil 

29import lvmcache 

30import scsiutil 

31import lock 

32import os 

33import sys 

34import time 

35import errno 

36import xs_errors 

37import cleanup 

38import blktap2 

39from journaler import Journaler 

40from refcounter import RefCounter 

41from ipc import IPCFlag 

42from constants import NS_PREFIX_LVM, VG_LOCATION, VG_PREFIX, CBT_BLOCK_SIZE 

43from cowutil import CowUtil, getCowUtil, getImageStringFromVdiType, getVdiTypeFromImageFormat 

44from lvmcowutil import LV_PREFIX, LvmCowUtil 

45from lvmanager import LVActivator 

46from vditype import VdiType 

47import XenAPI # pylint: disable=import-error 

48import re 

49from srmetadata import ALLOCATION_TAG, NAME_LABEL_TAG, NAME_DESCRIPTION_TAG, \ 

50 UUID_TAG, IS_A_SNAPSHOT_TAG, SNAPSHOT_OF_TAG, TYPE_TAG, VDI_TYPE_TAG, \ 

51 READ_ONLY_TAG, MANAGED_TAG, SNAPSHOT_TIME_TAG, METADATA_OF_POOL_TAG, \ 

52 LVMMetadataHandler, METADATA_OBJECT_TYPE_VDI, \ 

53 METADATA_OBJECT_TYPE_SR, METADATA_UPDATE_OBJECT_TYPE_TAG 

54from metadata import retrieveXMLfromFile, _parseXML 

55from xmlrpc.client import DateTime 

56import glob 

57from constants import CBTLOG_TAG 

58from fairlock import Fairlock 

59DEV_MAPPER_ROOT = os.path.join('/dev/mapper', VG_PREFIX) 

60 

61geneology: Dict[str, List[str]] = {} 

62CAPABILITIES = ["SR_PROBE", "SR_UPDATE", "SR_TRIM", 

63 "VDI_CREATE", "VDI_DELETE", "VDI_ATTACH", "VDI_DETACH", "VDI_MIRROR", 

64 "VDI_CLONE", "VDI_SNAPSHOT", "VDI_RESIZE", "ATOMIC_PAUSE", 

65 "VDI_RESET_ON_BOOT/2", "VDI_UPDATE", "VDI_CONFIG_CBT", 

66 "VDI_ACTIVATE", "VDI_DEACTIVATE"] 

67 

68CONFIGURATION = [['device', 'local device path (required) (e.g. /dev/sda3)']] 

69 

70DRIVER_INFO = { 

71 'name': 'Local VHD and QCOW2 on LVM', 

72 'description': 'SR plugin which represents disks as VHD and QCOW2 disks on ' + \ 

73 'Logical Volumes within a locally-attached Volume Group', 

74 'vendor': 'XenSource Inc', 

75 'copyright': '(C) 2008 XenSource Inc', 

76 'driver_version': '1.0', 

77 'required_api_version': '1.0', 

78 'capabilities': CAPABILITIES, 

79 'configuration': CONFIGURATION 

80 } 

81 

82OPS_EXCLUSIVE = [ 

83 "sr_create", "sr_delete", "sr_attach", "sr_detach", "sr_scan", 

84 "sr_update", "vdi_create", "vdi_delete", "vdi_resize", "vdi_snapshot", 

85 "vdi_clone"] 

86 

87# Log if snapshot pauses VM for more than this many seconds 

88LONG_SNAPTIME = 60 

89 

90class LVMSR(SR.SR): 

91 DRIVER_TYPE = 'lvhd' 

92 

93 PROVISIONING_TYPES = ["thin", "thick"] 

94 PROVISIONING_DEFAULT = "thick" 

95 THIN_PLUGIN = "lvhd-thin" 

96 

97 PLUGIN_ON_SLAVE = "on-slave" 

98 

99 FLAG_USE_VHD = "use_vhd" 

100 MDVOLUME_NAME = "MGT" 

101 

102 ALLOCATION_QUANTUM = "allocation_quantum" 

103 INITIAL_ALLOCATION = "initial_allocation" 

104 

105 LOCK_RETRY_INTERVAL = 3 

106 LOCK_RETRY_ATTEMPTS = 10 

107 

108 TEST_MODE_KEY = "testmode" 

109 TEST_MODE_VHD_FAIL_REPARENT_BEGIN = "vhd_fail_reparent_begin" 

110 TEST_MODE_VHD_FAIL_REPARENT_LOCATOR = "vhd_fail_reparent_locator" 

111 TEST_MODE_VHD_FAIL_REPARENT_END = "vhd_fail_reparent_end" 

112 TEST_MODE_VHD_FAIL_RESIZE_BEGIN = "vhd_fail_resize_begin" 

113 TEST_MODE_VHD_FAIL_RESIZE_DATA = "vhd_fail_resize_data" 

114 TEST_MODE_VHD_FAIL_RESIZE_METADATA = "vhd_fail_resize_metadata" 

115 TEST_MODE_VHD_FAIL_RESIZE_END = "vhd_fail_resize_end" 

116 

117 ENV_VAR_VHD_TEST = { 

118 TEST_MODE_VHD_FAIL_REPARENT_BEGIN: 

119 "VHD_UTIL_TEST_FAIL_REPARENT_BEGIN", 

120 TEST_MODE_VHD_FAIL_REPARENT_LOCATOR: 

121 "VHD_UTIL_TEST_FAIL_REPARENT_LOCATOR", 

122 TEST_MODE_VHD_FAIL_REPARENT_END: 

123 "VHD_UTIL_TEST_FAIL_REPARENT_END", 

124 TEST_MODE_VHD_FAIL_RESIZE_BEGIN: 

125 "VHD_UTIL_TEST_FAIL_RESIZE_BEGIN", 

126 TEST_MODE_VHD_FAIL_RESIZE_DATA: 

127 "VHD_UTIL_TEST_FAIL_RESIZE_DATA_MOVED", 

128 TEST_MODE_VHD_FAIL_RESIZE_METADATA: 

129 "VHD_UTIL_TEST_FAIL_RESIZE_METADATA_MOVED", 

130 TEST_MODE_VHD_FAIL_RESIZE_END: 

131 "VHD_UTIL_TEST_FAIL_RESIZE_END" 

132 } 

133 testMode = "" 

134 

135 legacyMode = True 

136 

137 @override 

138 @staticmethod 

139 def handles(type) -> bool: 

140 """Returns True if this SR class understands the given dconf string""" 

141 # we can pose as LVMSR or EXTSR for compatibility purposes 

142 if __name__ == '__main__': 

143 name = sys.argv[0] 

144 else: 

145 name = __name__ 

146 if name.endswith("LVMSR"): 

147 return type == "lvm" 

148 elif name.endswith("EXTSR"): 

149 return type == "ext" 

150 return type == LVMSR.DRIVER_TYPE 

151 

152 def __init__(self, srcmd, sr_uuid): 

153 SR.SR.__init__(self, srcmd, sr_uuid) 

154 self._init_image_formats() 

155 

156 @override 

157 def load(self, sr_uuid) -> None: 

158 self.ops_exclusive = OPS_EXCLUSIVE 

159 

160 self.isMaster = False 

161 if 'SRmaster' in self.dconf and self.dconf['SRmaster'] == 'true': 

162 self.isMaster = True 

163 

164 self.lock = lock.Lock(lock.LOCK_TYPE_SR, self.uuid) 

165 self.sr_vditype = SR.DEFAULT_TAP 

166 self.uuid = sr_uuid 

167 self.vgname = VG_PREFIX + self.uuid 

168 self.path = os.path.join(VG_LOCATION, self.vgname) 

169 self.mdpath = os.path.join(self.path, self.MDVOLUME_NAME) 

170 self.provision = self.PROVISIONING_DEFAULT 

171 

172 has_sr_ref = self.srcmd.params.get("sr_ref") 

173 if has_sr_ref: 

174 self.other_conf = self.session.xenapi.SR.get_other_config(self.sr_ref) 

175 else: 

176 self.other_conf = None 

177 

178 self.lvm_conf = None 

179 if self.other_conf: 

180 self.lvm_conf = self.other_conf.get('lvm-conf') 

181 

182 try: 

183 self.lvmCache = lvmcache.LVMCache(self.vgname, self.lvm_conf) 

184 except: 

185 raise xs_errors.XenError('SRUnavailable', \ 

186 opterr='Failed to initialise the LVMCache') 

187 self.lvActivator = LVActivator(self.uuid, self.lvmCache) 

188 self.journaler = Journaler(self.lvmCache) 

189 if not has_sr_ref: 

190 return # must be a probe call 

191 # Test for thick vs thin provisioning conf parameter 

192 if 'allocation' in self.dconf: 192 ↛ 193line 192 didn't jump to line 193, because the condition on line 192 was never true

193 if self.dconf['allocation'] in self.PROVISIONING_TYPES: 

194 self.provision = self.dconf['allocation'] 

195 else: 

196 raise xs_errors.XenError('InvalidArg', \ 

197 opterr='Allocation parameter must be one of %s' % self.PROVISIONING_TYPES) 

198 

199 if self.other_conf.get(self.TEST_MODE_KEY): 199 ↛ 203line 199 didn't jump to line 203, because the condition on line 199 was never false

200 self.testMode = self.other_conf[self.TEST_MODE_KEY] 

201 self._prepareTestMode() 

202 

203 self.sm_config = self.session.xenapi.SR.get_sm_config(self.sr_ref) 

204 # sm_config flag overrides PBD, if any 

205 if self.sm_config.get('allocation') in self.PROVISIONING_TYPES: 

206 self.provision = self.sm_config.get('allocation') 

207 

208 if self.sm_config.get(self.FLAG_USE_VHD) == "true": 

209 self.legacyMode = False 

210 

211 if lvutil._checkVG(self.vgname): 

212 if self.isMaster and not self.cmd in ["vdi_attach", "vdi_detach", 212 ↛ 215line 212 didn't jump to line 215, because the condition on line 212 was never false

213 "vdi_activate", "vdi_deactivate"]: 

214 self._undoAllJournals() 

215 if not self.cmd in ["sr_attach", "sr_probe"]: 

216 self._checkMetadataVolume() 

217 

218 self.mdexists = False 

219 

220 # get a VDI -> TYPE map from the storage 

221 contains_uuid_regex = \ 

222 re.compile("^.*[0-9a-f]{8}-(([0-9a-f]{4})-){3}[0-9a-f]{12}.*") 

223 self.storageVDIs = {} 

224 

225 for key in self.lvmCache.lvs.keys(): 225 ↛ 227line 225 didn't jump to line 227, because the loop on line 225 never started

226 # if the lvname has a uuid in it 

227 type = None 

228 vdi = None 

229 if contains_uuid_regex.search(key) is not None: 

230 for vdi_type, prefix in LV_PREFIX.items(): 

231 if key.startswith(prefix): 

232 vdi = key[len(prefix):] 

233 self.storageVDIs[vdi] = vdi_type 

234 break 

235 

236 # check if metadata volume exists 

237 try: 

238 self.mdexists = self.lvmCache.checkLV(self.MDVOLUME_NAME) 

239 except: 

240 pass 

241 

242 @override 

243 def cleanup(self) -> None: 

244 # we don't need to hold the lock to dec refcounts of activated LVs 

245 if not self.lvActivator.deactivateAll(): 245 ↛ 246line 245 didn't jump to line 246, because the condition on line 245 was never true

246 raise util.SMException("failed to deactivate LVs") 

247 

248 def updateSRMetadata(self, allocation): 

249 try: 

250 # Add SR specific SR metadata 

251 sr_info = \ 

252 {ALLOCATION_TAG: allocation, 

253 UUID_TAG: self.uuid, 

254 NAME_LABEL_TAG: util.to_plain_string(self.session.xenapi.SR.get_name_label(self.sr_ref)), 

255 NAME_DESCRIPTION_TAG: util.to_plain_string(self.session.xenapi.SR.get_name_description(self.sr_ref)) 

256 } 

257 

258 vdi_info = {} 

259 for vdi in self.session.xenapi.SR.get_VDIs(self.sr_ref): 

260 vdi_uuid = self.session.xenapi.VDI.get_uuid(vdi) 

261 

262 vdi_type = self.session.xenapi.VDI.get_sm_config(vdi).get('vdi_type') 

263 if not vdi_type: 

264 raise xs_errors.XenError('MetadataError', opterr=f"Missing `vdi_type` for VDI {vdi_uuid}") 

265 

266 # Create the VDI entry in the SR metadata 

267 vdi_info[vdi_uuid] = \ 

268 { 

269 UUID_TAG: vdi_uuid, 

270 NAME_LABEL_TAG: util.to_plain_string(self.session.xenapi.VDI.get_name_label(vdi)), 

271 NAME_DESCRIPTION_TAG: util.to_plain_string(self.session.xenapi.VDI.get_name_description(vdi)), 

272 IS_A_SNAPSHOT_TAG: \ 

273 int(self.session.xenapi.VDI.get_is_a_snapshot(vdi)), 

274 SNAPSHOT_OF_TAG: \ 

275 self.session.xenapi.VDI.get_snapshot_of(vdi), 

276 SNAPSHOT_TIME_TAG: \ 

277 self.session.xenapi.VDI.get_snapshot_time(vdi), 

278 TYPE_TAG: \ 

279 self.session.xenapi.VDI.get_type(vdi), 

280 VDI_TYPE_TAG: \ 

281 vdi_type, 

282 READ_ONLY_TAG: \ 

283 int(self.session.xenapi.VDI.get_read_only(vdi)), 

284 METADATA_OF_POOL_TAG: \ 

285 self.session.xenapi.VDI.get_metadata_of_pool(vdi), 

286 MANAGED_TAG: \ 

287 int(self.session.xenapi.VDI.get_managed(vdi)) 

288 } 

289 LVMMetadataHandler(self.mdpath).writeMetadata(sr_info, vdi_info) 

290 

291 except Exception as e: 

292 raise xs_errors.XenError('MetadataError', \ 

293 opterr='Error upgrading SR Metadata: %s' % str(e)) 

294 

295 def syncMetadataAndStorage(self): 

296 try: 

297 # if a VDI is present in the metadata but not in the storage 

298 # then delete it from the metadata 

299 vdi_info = LVMMetadataHandler(self.mdpath, False).getMetadata()[1] 

300 for vdi in list(vdi_info.keys()): 

301 update_map = {} 

302 if not vdi_info[vdi][UUID_TAG] in set(self.storageVDIs.keys()): 302 ↛ 309line 302 didn't jump to line 309, because the condition on line 302 was never false

303 # delete this from metadata 

304 LVMMetadataHandler(self.mdpath). \ 

305 deleteVdiFromMetadata(vdi_info[vdi][UUID_TAG]) 

306 else: 

307 # search for this in the metadata, compare types 

308 # self.storageVDIs is a map of vdi_uuid to vdi_type 

309 if vdi_info[vdi][VDI_TYPE_TAG] != \ 

310 self.storageVDIs[vdi_info[vdi][UUID_TAG]]: 

311 # storage type takes authority 

312 update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] \ 

313 = METADATA_OBJECT_TYPE_VDI 

314 update_map[UUID_TAG] = vdi_info[vdi][UUID_TAG] 

315 update_map[VDI_TYPE_TAG] = \ 

316 self.storageVDIs[vdi_info[vdi][UUID_TAG]] 

317 LVMMetadataHandler(self.mdpath) \ 

318 .updateMetadata(update_map) 

319 else: 

320 # This should never happen 

321 pass 

322 

323 except Exception as e: 

324 raise xs_errors.XenError('MetadataError', \ 

325 opterr='Error synching SR Metadata and storage: %s' % str(e)) 

326 

327 def syncMetadataAndXapi(self): 

328 try: 

329 # get metadata 

330 (sr_info, vdi_info) = \ 

331 LVMMetadataHandler(self.mdpath, False).getMetadata() 

332 

333 # First synch SR parameters 

334 self.update(self.uuid) 

335 

336 # Now update the VDI information in the metadata if required 

337 for vdi_offset in vdi_info.keys(): 

338 try: 

339 vdi_ref = \ 

340 self.session.xenapi.VDI.get_by_uuid( \ 

341 vdi_info[vdi_offset][UUID_TAG]) 

342 except: 

343 # may be the VDI is not in XAPI yet dont bother 

344 continue 

345 

346 new_name_label = util.to_plain_string(self.session.xenapi.VDI.get_name_label(vdi_ref)) 

347 new_name_description = util.to_plain_string(self.session.xenapi.VDI.get_name_description(vdi_ref)) 

348 

349 if vdi_info[vdi_offset][NAME_LABEL_TAG] != new_name_label or \ 

350 vdi_info[vdi_offset][NAME_DESCRIPTION_TAG] != \ 

351 new_name_description: 

352 update_map = {} 

353 update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] = \ 

354 METADATA_OBJECT_TYPE_VDI 

355 update_map[UUID_TAG] = vdi_info[vdi_offset][UUID_TAG] 

356 update_map[NAME_LABEL_TAG] = new_name_label 

357 update_map[NAME_DESCRIPTION_TAG] = new_name_description 

358 LVMMetadataHandler(self.mdpath) \ 

359 .updateMetadata(update_map) 

360 except Exception as e: 

361 raise xs_errors.XenError('MetadataError', \ 

362 opterr='Error synching SR Metadata and XAPI: %s' % str(e)) 

363 

364 def _checkMetadataVolume(self): 

365 util.SMlog("Entering _checkMetadataVolume") 

366 self.mdexists = self.lvmCache.checkLV(self.MDVOLUME_NAME) 

367 if self.isMaster: 367 ↛ 383line 367 didn't jump to line 383, because the condition on line 367 was never false

368 if self.mdexists and self.cmd == "sr_attach": 

369 try: 

370 # activate the management volume 

371 # will be deactivated at detach time 

372 self.lvmCache.activateNoRefcount(self.MDVOLUME_NAME) 

373 self._synchSmConfigWithMetaData() 

374 util.SMlog("Sync SR metadata and the state on the storage.") 

375 self.syncMetadataAndStorage() 

376 self.syncMetadataAndXapi() 

377 except Exception as e: 

378 util.SMlog("Exception in _checkMetadataVolume, " \ 

379 "Error: %s." % str(e)) 

380 elif not self.mdexists and not self.legacyMode: 

381 self._introduceMetaDataVolume() 

382 

383 if self.mdexists: 

384 self.legacyMode = False 

385 

386 def _synchSmConfigWithMetaData(self): 

387 util.SMlog("Synching sm-config with metadata volume") 

388 

389 try: 

390 # get SR info from metadata 

391 sr_info = {} 

392 map = {} 

393 sr_info = LVMMetadataHandler(self.mdpath, False).getMetadata()[0] 

394 

395 if sr_info == {}: 395 ↛ 396line 395 didn't jump to line 396, because the condition on line 395 was never true

396 raise Exception("Failed to get SR information from metadata.") 

397 

398 if "allocation" in sr_info: 398 ↛ 402line 398 didn't jump to line 402, because the condition on line 398 was never false

399 self.provision = sr_info.get("allocation") 

400 map['allocation'] = sr_info.get("allocation") 

401 else: 

402 raise Exception("Allocation key not found in SR metadata. " 

403 "SR info found: %s" % sr_info) 

404 

405 except Exception as e: 

406 raise xs_errors.XenError( 

407 'MetadataError', 

408 opterr='Error reading SR params from ' 

409 'metadata Volume: %s' % str(e)) 

410 try: 

411 map[self.FLAG_USE_VHD] = 'true' 

412 self.session.xenapi.SR.set_sm_config(self.sr_ref, map) 

413 except: 

414 raise xs_errors.XenError( 

415 'MetadataError', 

416 opterr='Error updating sm_config key') 

417 

418 def _introduceMetaDataVolume(self): 

419 util.SMlog("Creating Metadata volume") 

420 try: 

421 config = {} 

422 self.lvmCache.create(self.MDVOLUME_NAME, 4 * 1024 * 1024) 

423 

424 # activate the management volume, will be deactivated at detach time 

425 self.lvmCache.activateNoRefcount(self.MDVOLUME_NAME) 

426 

427 name_label = util.to_plain_string( \ 

428 self.session.xenapi.SR.get_name_label(self.sr_ref)) 

429 name_description = util.to_plain_string( \ 

430 self.session.xenapi.SR.get_name_description(self.sr_ref)) 

431 config[self.FLAG_USE_VHD] = "true" 

432 config['allocation'] = self.provision 

433 self.session.xenapi.SR.set_sm_config(self.sr_ref, config) 

434 

435 # Add the SR metadata 

436 self.updateSRMetadata(self.provision) 

437 except Exception as e: 

438 raise xs_errors.XenError('MetadataError', \ 

439 opterr='Error introducing Metadata Volume: %s' % str(e)) 

440 

441 def _removeMetadataVolume(self): 

442 if self.mdexists: 

443 try: 

444 self.lvmCache.remove(self.MDVOLUME_NAME) 

445 except: 

446 raise xs_errors.XenError('MetadataError', \ 

447 opterr='Failed to delete MGT Volume') 

448 

449 def _refresh_size(self): 

450 """ 

451 Refreshs the size of the backing device. 

452 Return true if all paths/devices agree on the same size. 

453 """ 

454 if hasattr(self, 'SCSIid'): 454 ↛ 456line 454 didn't jump to line 456, because the condition on line 454 was never true

455 # LVMoHBASR, LVMoISCSISR 

456 return scsiutil.refresh_lun_size_by_SCSIid(getattr(self, 'SCSIid')) 

457 else: 

458 # LVMSR 

459 devices = self.dconf['device'].split(',') 

460 scsiutil.refreshdev(devices) 

461 return True 

462 

463 def _expand_size(self): 

464 """ 

465 Expands the size of the SR by growing into additional availiable 

466 space, if extra space is availiable on the backing device. 

467 Needs to be called after a successful call of _refresh_size. 

468 """ 

469 currentvgsize = lvutil._getVGstats(self.vgname)['physical_size'] 

470 # We are comparing PV- with VG-sizes that are aligned. Need a threshold 

471 resizethreshold = 100 * 1024 * 1024 # 100MB 

472 devices = self.dconf['device'].split(',') 

473 totaldevicesize = 0 

474 for device in devices: 

475 totaldevicesize = totaldevicesize + scsiutil.getsize(device) 

476 if totaldevicesize >= (currentvgsize + resizethreshold): 

477 try: 

478 if hasattr(self, 'SCSIid'): 478 ↛ 480line 478 didn't jump to line 480, because the condition on line 478 was never true

479 # LVMoHBASR, LVMoISCSISR might have slaves 

480 scsiutil.refresh_lun_size_by_SCSIid_on_slaves(self.session, 

481 getattr(self, 'SCSIid')) 

482 util.SMlog("LVMSR._expand_size for %s will resize the pv." % 

483 self.uuid) 

484 for pv in lvutil.get_pv_for_vg(self.vgname): 

485 lvutil.resizePV(pv) 

486 except: 

487 util.logException("LVMSR._expand_size for %s failed to resize" 

488 " the PV" % self.uuid) 

489 

490 @override 

491 @deviceCheck 

492 def create(self, uuid, size) -> None: 

493 util.SMlog("LVMSR.create for %s" % self.uuid) 

494 if not self.isMaster: 

495 util.SMlog('sr_create blocked for non-master') 

496 raise xs_errors.XenError('LVMMaster') 

497 

498 if lvutil._checkVG(self.vgname): 

499 raise xs_errors.XenError('SRExists') 

500 

501 # Check none of the devices already in use by other PBDs 

502 if util.test_hostPBD_devs(self.session, uuid, self.dconf['device']): 

503 raise xs_errors.XenError('SRInUse') 

504 

505 # Check serial number entry in SR records 

506 for dev in self.dconf['device'].split(','): 

507 if util.test_scsiserial(self.session, dev): 

508 raise xs_errors.XenError('SRInUse') 

509 

510 lvutil.createVG(self.dconf['device'], self.vgname) 

511 

512 #Update serial number string 

513 scsiutil.add_serial_record(self.session, self.sr_ref, \ 

514 scsiutil.devlist_to_serialstring(self.dconf['device'].split(','))) 

515 

516 # since this is an SR.create turn off legacy mode 

517 self.session.xenapi.SR.add_to_sm_config(self.sr_ref, \ 

518 self.FLAG_USE_VHD, 'true') 

519 

520 @override 

521 def delete(self, uuid) -> None: 

522 util.SMlog("LVMSR.delete for %s" % self.uuid) 

523 if not self.isMaster: 

524 raise xs_errors.XenError('LVMMaster') 

525 cleanup.gc_force(self.session, self.uuid) 

526 

527 success = True 

528 for fileName in glob.glob(DEV_MAPPER_ROOT + '*'): 

529 if util.extractSRFromDevMapper(fileName) != self.uuid: 

530 continue 

531 

532 if util.doesFileHaveOpenHandles(fileName): 

533 util.SMlog("LVMSR.delete: The dev mapper entry %s has open " \ 

534 "handles" % fileName) 

535 success = False 

536 continue 

537 

538 # Now attempt to remove the dev mapper entry 

539 if not lvutil.removeDevMapperEntry(fileName, False): 

540 success = False 

541 continue 

542 

543 try: 

544 lvname = os.path.basename(fileName.replace('-', '/'). \ 

545 replace('//', '-')) 

546 lpath = os.path.join(self.path, lvname) 

547 os.unlink(lpath) 

548 except OSError as e: 

549 if e.errno != errno.ENOENT: 

550 util.SMlog("LVMSR.delete: failed to remove the symlink for " \ 

551 "file %s. Error: %s" % (fileName, str(e))) 

552 success = False 

553 

554 if success: 

555 try: 

556 if util.pathexists(self.path): 

557 os.rmdir(self.path) 

558 except Exception as e: 

559 util.SMlog("LVMSR.delete: failed to remove the symlink " \ 

560 "directory %s. Error: %s" % (self.path, str(e))) 

561 success = False 

562 

563 self._removeMetadataVolume() 

564 self.lvmCache.refresh() 

565 if LvmCowUtil.getVolumeInfo(self.lvmCache): 

566 raise xs_errors.XenError('SRNotEmpty') 

567 

568 if not success: 

569 raise Exception("LVMSR delete failed, please refer to the log " \ 

570 "for details.") 

571 

572 lvutil.removeVG(self.dconf['device'], self.vgname) 

573 self._cleanup() 

574 

575 @override 

576 def attach(self, uuid) -> None: 

577 util.SMlog("LVMSR.attach for %s" % self.uuid) 

578 

579 self._cleanup(True) # in case of host crashes, if detach wasn't called 

580 

581 if not util.match_uuid(self.uuid) or not lvutil._checkVG(self.vgname): 581 ↛ 582line 581 didn't jump to line 582, because the condition on line 581 was never true

582 raise xs_errors.XenError('SRUnavailable', \ 

583 opterr='no such volume group: %s' % self.vgname) 

584 

585 # Refresh the metadata status 

586 self._checkMetadataVolume() 

587 

588 refreshsizeok = self._refresh_size() 

589 

590 if self.isMaster: 590 ↛ 601line 590 didn't jump to line 601, because the condition on line 590 was never false

591 if refreshsizeok: 591 ↛ 595line 591 didn't jump to line 595, because the condition on line 591 was never false

592 self._expand_size() 

593 

594 # Update SCSIid string 

595 util.SMlog("Calling devlist_to_serial") 

596 scsiutil.add_serial_record( 

597 self.session, self.sr_ref, 

598 scsiutil.devlist_to_serialstring(self.dconf['device'].split(','))) 

599 

600 # Test Legacy Mode Flag and update if COW volumes exist 

601 if self.isMaster and self.legacyMode: 601 ↛ 602line 601 didn't jump to line 602, because the condition on line 601 was never true

602 vdiInfo = LvmCowUtil.getVDIInfo(self.lvmCache) 

603 for uuid, info in vdiInfo.items(): 

604 if VdiType.isCowImage(info.vdiType): 

605 self.legacyMode = False 

606 map = self.session.xenapi.SR.get_sm_config(self.sr_ref) 

607 self._introduceMetaDataVolume() 

608 break 

609 

610 # Set the block scheduler 

611 for dev in self.dconf['device'].split(','): 

612 self.block_setscheduler(dev) 

613 

614 @override 

615 def detach(self, uuid) -> None: 

616 util.SMlog("LVMSR.detach for %s" % self.uuid) 

617 cleanup.abort(self.uuid) 

618 

619 # Do a best effort cleanup of the dev mapper entries 

620 # go through all devmapper entries for this VG 

621 success = True 

622 for fileName in glob.glob(DEV_MAPPER_ROOT + '*'): 

623 if util.extractSRFromDevMapper(fileName) != self.uuid: 623 ↛ 624line 623 didn't jump to line 624, because the condition on line 623 was never true

624 continue 

625 

626 with Fairlock('devicemapper'): 

627 # check if any file has open handles 

628 if util.doesFileHaveOpenHandles(fileName): 

629 # if yes, log this and signal failure 

630 util.SMlog( 

631 f"LVMSR.detach: The dev mapper entry {fileName} has " 

632 "open handles") 

633 success = False 

634 continue 

635 

636 # Now attempt to remove the dev mapper entry 

637 if not lvutil.removeDevMapperEntry(fileName, False): 637 ↛ 638line 637 didn't jump to line 638, because the condition on line 637 was never true

638 success = False 

639 continue 

640 

641 # also remove the symlinks from /dev/VG-XenStorage-SRUUID/* 

642 try: 

643 lvname = os.path.basename(fileName.replace('-', '/'). \ 

644 replace('//', '-')) 

645 lvname = os.path.join(self.path, lvname) 

646 util.force_unlink(lvname) 

647 except Exception as e: 

648 util.SMlog("LVMSR.detach: failed to remove the symlink for " \ 

649 "file %s. Error: %s" % (fileName, str(e))) 

650 success = False 

651 

652 # now remove the directory where the symlinks are 

653 # this should pass as the directory should be empty by now 

654 if success: 

655 try: 

656 if util.pathexists(self.path): 656 ↛ 657line 656 didn't jump to line 657, because the condition on line 656 was never true

657 os.rmdir(self.path) 

658 except Exception as e: 

659 util.SMlog("LVMSR.detach: failed to remove the symlink " \ 

660 "directory %s. Error: %s" % (self.path, str(e))) 

661 success = False 

662 

663 if not success: 

664 raise Exception("SR detach failed, please refer to the log " \ 

665 "for details.") 

666 

667 # Don't delete lock files on the master as it will break the locking 

668 # between SM and any GC thread that survives through SR.detach. 

669 # However, we should still delete lock files on slaves as it is the 

670 # only place to do so. 

671 self._cleanup(self.isMaster) 

672 

673 @override 

674 def forget_vdi(self, uuid) -> None: 

675 if not self.legacyMode: 

676 LVMMetadataHandler(self.mdpath).deleteVdiFromMetadata(uuid) 

677 super(LVMSR, self).forget_vdi(uuid) 

678 

679 @override 

680 def scan(self, uuid) -> None: 

681 activated_lvs = set() 

682 try: 

683 util.SMlog("LVMSR.scan for %s" % self.uuid) 

684 if not self.isMaster: 684 ↛ 685line 684 didn't jump to line 685, because the condition on line 684 was never true

685 util.SMlog('sr_scan blocked for non-master') 

686 raise xs_errors.XenError('LVMMaster') 

687 

688 if self._refresh_size(): 688 ↛ 690line 688 didn't jump to line 690, because the condition on line 688 was never false

689 self._expand_size() 

690 self.lvmCache.refresh() 

691 cbt_vdis = self.lvmCache.getTagged(CBTLOG_TAG) 

692 self._loadvdis() 

693 stats = lvutil._getVGstats(self.vgname) 

694 self.physical_size = stats['physical_size'] 

695 self.physical_utilisation = stats['physical_utilisation'] 

696 

697 # Now check if there are any VDIs in the metadata, which are not in 

698 # XAPI 

699 if self.mdexists: 699 ↛ 818line 699 didn't jump to line 818, because the condition on line 699 was never false

700 vdiToSnaps: Dict[str, List[str]] = {} 

701 # get VDIs from XAPI 

702 vdis = self.session.xenapi.SR.get_VDIs(self.sr_ref) 

703 vdi_uuids = set([]) 

704 for vdi in vdis: 

705 vdi_uuids.add(self.session.xenapi.VDI.get_uuid(vdi)) 

706 

707 info = LVMMetadataHandler(self.mdpath, False).getMetadata()[1] 

708 

709 for vdi in list(info.keys()): 

710 vdi_uuid = info[vdi][UUID_TAG] 

711 if bool(int(info[vdi][IS_A_SNAPSHOT_TAG])): 711 ↛ 712line 711 didn't jump to line 712, because the condition on line 711 was never true

712 if info[vdi][SNAPSHOT_OF_TAG] in vdiToSnaps: 

713 vdiToSnaps[info[vdi][SNAPSHOT_OF_TAG]].append(vdi_uuid) 

714 else: 

715 vdiToSnaps[info[vdi][SNAPSHOT_OF_TAG]] = [vdi_uuid] 

716 

717 if vdi_uuid not in vdi_uuids: 

718 vdi_type = info[vdi][VDI_TYPE_TAG] 

719 sm_config = {} 

720 sm_config['vdi_type'] = vdi_type 

721 lvname = "%s%s" % (LV_PREFIX[sm_config['vdi_type']], vdi_uuid) 

722 if not self.lvmCache.checkLV(lvname): 

723 util.SMlog("VDI %s is present in metadata but " 

724 "its LV %s does not exist in the VG; " 

725 "removing from metadata." % 

726 (vdi_uuid, lvname)) 

727 LVMMetadataHandler(self.mdpath). \ 

728 deleteVdiFromMetadata(vdi_uuid) 

729 continue 

730 util.SMlog("Introduce VDI %s as it is present in " \ 

731 "metadata and not in XAPI." % vdi_uuid) 

732 self.lvActivator.activate( 

733 vdi_uuid, lvname, LVActivator.NORMAL) 

734 activated_lvs.add(vdi_uuid) 

735 lvPath = os.path.join(self.path, lvname) 

736 

737 if not VdiType.isCowImage(vdi_type): 

738 size = self.lvmCache.getSize(LV_PREFIX[vdi_type] + vdi_uuid) 

739 utilisation = \ 

740 util.roundup(lvutil.LVM_SIZE_INCREMENT, 

741 int(size)) 

742 else: 

743 cowutil = getCowUtil(vdi_type) 

744 lvmcowutil = LvmCowUtil(cowutil) 

745 

746 parent = cowutil.getParentNoCheck(lvPath) 

747 

748 if parent is not None: 748 ↛ 749line 748 didn't jump to line 749, because the condition on line 748 was never true

749 sm_config['vhd-parent'] = parent[parent.find('-') + 1:] 

750 size = cowutil.getSizeVirt(lvPath) 

751 if self.provision == "thin": 751 ↛ 752line 751 didn't jump to line 752, because the condition on line 751 was never true

752 utilisation = util.roundup( 

753 lvutil.LVM_SIZE_INCREMENT, 

754 cowutil.calcOverheadEmpty(max(size, cowutil.getDefaultPreallocationSizeVirt())) 

755 ) 

756 else: 

757 utilisation = lvmcowutil.calcVolumeSize(int(size)) 

758 

759 vdi_ref = self.session.xenapi.VDI.db_introduce( 

760 vdi_uuid, 

761 info[vdi][NAME_LABEL_TAG], 

762 info[vdi][NAME_DESCRIPTION_TAG], 

763 self.sr_ref, 

764 info[vdi][TYPE_TAG], 

765 False, 

766 bool(int(info[vdi][READ_ONLY_TAG])), 

767 {}, 

768 vdi_uuid, 

769 {}, 

770 sm_config) 

771 

772 self.session.xenapi.VDI.set_managed(vdi_ref, 

773 bool(int(info[vdi][MANAGED_TAG]))) 

774 self.session.xenapi.VDI.set_virtual_size(vdi_ref, 

775 str(size)) 

776 self.session.xenapi.VDI.set_physical_utilisation( \ 

777 vdi_ref, str(utilisation)) 

778 self.session.xenapi.VDI.set_is_a_snapshot( \ 

779 vdi_ref, bool(int(info[vdi][IS_A_SNAPSHOT_TAG]))) 

780 if bool(int(info[vdi][IS_A_SNAPSHOT_TAG])): 780 ↛ 781line 780 didn't jump to line 781, because the condition on line 780 was never true

781 self.session.xenapi.VDI.set_snapshot_time( \ 

782 vdi_ref, DateTime(info[vdi][SNAPSHOT_TIME_TAG])) 

783 if info[vdi][TYPE_TAG] == 'metadata': 783 ↛ 784line 783 didn't jump to line 784, because the condition on line 783 was never true

784 self.session.xenapi.VDI.set_metadata_of_pool( \ 

785 vdi_ref, info[vdi][METADATA_OF_POOL_TAG]) 

786 

787 # Update CBT status of disks either just added 

788 # or already in XAPI 

789 cbt_logname = "%s.%s" % (vdi_uuid, CBTLOG_TAG) 

790 if cbt_logname in cbt_vdis: 790 ↛ 791line 790 didn't jump to line 791, because the condition on line 790 was never true

791 vdi_ref = self.session.xenapi.VDI.get_by_uuid(vdi_uuid) 

792 self.session.xenapi.VDI.set_cbt_enabled(vdi_ref, True) 

793 # For existing VDIs, update local state too 

794 # Scan in base class SR updates existing VDIs 

795 # again based on local states 

796 if vdi_uuid in self.vdis: 

797 self.vdis[vdi_uuid].cbt_enabled = True 

798 cbt_vdis.remove(cbt_logname) 

799 

800 # Now set the snapshot statuses correctly in XAPI 

801 for srcvdi in vdiToSnaps.keys(): 801 ↛ 802line 801 didn't jump to line 802, because the loop on line 801 never started

802 try: 

803 srcref = self.session.xenapi.VDI.get_by_uuid(srcvdi) 

804 except: 

805 # the source VDI no longer exists, continue 

806 continue 

807 

808 for snapvdi in vdiToSnaps[srcvdi]: 

809 try: 

810 # this might fail in cases where its already set 

811 snapref = \ 

812 self.session.xenapi.VDI.get_by_uuid(snapvdi) 

813 self.session.xenapi.VDI.set_snapshot_of(snapref, srcref) 

814 except Exception as e: 

815 util.SMlog("Setting snapshot failed. " \ 

816 "Error: %s" % str(e)) 

817 

818 if cbt_vdis: 818 ↛ 829line 818 didn't jump to line 829, because the condition on line 818 was never false

819 # If we have items remaining in this list, 

820 # they are cbt_metadata VDI that XAPI doesn't know about 

821 # Add them to self.vdis and they'll get added to the DB 

822 for cbt_vdi in cbt_vdis: 822 ↛ 823line 822 didn't jump to line 823, because the loop on line 822 never started

823 cbt_uuid = cbt_vdi.split(".")[0] 

824 new_vdi = self.vdi(cbt_uuid) 

825 new_vdi.ty = "cbt_metadata" 

826 new_vdi.cbt_enabled = True 

827 self.vdis[cbt_uuid] = new_vdi 

828 

829 super(LVMSR, self).scan(uuid) 

830 self._kickGC() 

831 

832 finally: 

833 for vdi in activated_lvs: 

834 self.lvActivator.deactivate( 

835 vdi, LVActivator.NORMAL, False) 

836 

837 @override 

838 def update(self, uuid) -> None: 

839 if not lvutil._checkVG(self.vgname): 839 ↛ 840line 839 didn't jump to line 840, because the condition on line 839 was never true

840 return 

841 self._updateStats(uuid, 0) 

842 

843 if self.legacyMode: 843 ↛ 844line 843 didn't jump to line 844, because the condition on line 843 was never true

844 return 

845 

846 # synch name_label in metadata with XAPI 

847 update_map = {} 

848 update_map = {METADATA_UPDATE_OBJECT_TYPE_TAG: \ 

849 METADATA_OBJECT_TYPE_SR, 

850 NAME_LABEL_TAG: util.to_plain_string( \ 

851 self.session.xenapi.SR.get_name_label(self.sr_ref)), 

852 NAME_DESCRIPTION_TAG: util.to_plain_string( \ 

853 self.session.xenapi.SR.get_name_description(self.sr_ref)) 

854 } 

855 LVMMetadataHandler(self.mdpath).updateMetadata(update_map) 

856 

857 def _updateStats(self, uuid, virtAllocDelta): 

858 valloc = int(self.session.xenapi.SR.get_virtual_allocation(self.sr_ref)) 

859 self.virtual_allocation = valloc + virtAllocDelta 

860 util.SMlog("Setting virtual_allocation of SR %s to %d" % 

861 (uuid, self.virtual_allocation)) 

862 stats = lvutil._getVGstats(self.vgname) 

863 self.physical_size = stats['physical_size'] 

864 self.physical_utilisation = stats['physical_utilisation'] 

865 self._db_update() 

866 

867 @override 

868 @deviceCheck 

869 def probe(self) -> str: 

870 return lvutil.srlist_toxml( 

871 lvutil.scan_srlist(VG_PREFIX, self.dconf['device']), 

872 VG_PREFIX, 

873 ('metadata' in self.srcmd.params['sr_sm_config'] and \ 

874 self.srcmd.params['sr_sm_config']['metadata'] == 'true')) 

875 

876 @override 

877 def vdi(self, uuid) -> VDI.VDI: 

878 return LVMVDI(self, uuid) 

879 

880 def _loadvdis(self): 

881 self.virtual_allocation = 0 

882 self.vdiInfo = LvmCowUtil.getVDIInfo(self.lvmCache) 

883 self.allVDIs = {} 

884 

885 for uuid, info in self.vdiInfo.items(): 

886 if uuid.startswith(cleanup.SR.TMP_RENAME_PREFIX): 886 ↛ 887line 886 didn't jump to line 887, because the condition on line 886 was never true

887 continue 

888 if info.scanError: 888 ↛ 889line 888 didn't jump to line 889, because the condition on line 888 was never true

889 raise xs_errors.XenError('VDIUnavailable', \ 

890 opterr='Error scanning VDI %s' % uuid) 

891 self.vdis[uuid] = self.allVDIs[uuid] = self.vdi(uuid) 

892 if not self.vdis[uuid].hidden: 892 ↛ 885line 892 didn't jump to line 885, because the condition on line 892 was never false

893 self.virtual_allocation += self.vdis[uuid].utilisation 

894 

895 for uuid, vdi in self.vdis.items(): 

896 if vdi.parent: 896 ↛ 897line 896 didn't jump to line 897, because the condition on line 896 was never true

897 if vdi.parent in self.vdis: 

898 self.vdis[vdi.parent].read_only = True 

899 if vdi.parent in geneology: 

900 geneology[vdi.parent].append(uuid) 

901 else: 

902 geneology[vdi.parent] = [uuid] 

903 

904 # Now remove all hidden leaf nodes to avoid introducing records that 

905 # will be GC'ed 

906 for uuid in list(self.vdis.keys()): 

907 if uuid not in geneology and self.vdis[uuid].hidden: 907 ↛ 908line 907 didn't jump to line 908, because the condition on line 907 was never true

908 util.SMlog("Scan found hidden leaf (%s), ignoring" % uuid) 

909 del self.vdis[uuid] 

910 

911 def _ensureSpaceAvailable(self, amount_needed): 

912 space_available = lvutil._getVGstats(self.vgname)['freespace'] 

913 if (space_available < amount_needed): 

914 util.SMlog("Not enough space! free space: %d, need: %d" % \ 

915 (space_available, amount_needed)) 

916 raise xs_errors.XenError('SRNoSpace') 

917 

918 def _handleInterruptedCloneOps(self): 

919 entries = self.journaler.getAll(LVMVDI.JRN_CLONE) 

920 for uuid, val in entries.items(): 920 ↛ 921line 920 didn't jump to line 921, because the loop on line 920 never started

921 util.fistpoint.activate("LVHDRT_clone_vdi_before_undo_clone", self.uuid) 

922 self._handleInterruptedCloneOp(uuid, val) 

923 util.fistpoint.activate("LVHDRT_clone_vdi_after_undo_clone", self.uuid) 

924 self.journaler.remove(LVMVDI.JRN_CLONE, uuid) 

925 

926 def _handleInterruptedCoalesceLeaf(self): 

927 entries = self.journaler.getAll(cleanup.VDI.JRN_LEAF) 

928 if len(entries) > 0: 928 ↛ 929line 928 didn't jump to line 929, because the condition on line 928 was never true

929 util.SMlog("*** INTERRUPTED COALESCE-LEAF OP DETECTED ***") 

930 cleanup.gc_force(self.session, self.uuid) 

931 self.lvmCache.refresh() 

932 

933 def _handleInterruptedCloneOp(self, origUuid, jval, forceUndo=False): 

934 """Either roll back or finalize the interrupted snapshot/clone 

935 operation. Rolling back is unsafe if the leaf images have already been 

936 in use and written to. However, it is always safe to roll back while 

937 we're still in the context of the failed snapshot operation since the 

938 VBD is paused for the duration of the operation""" 

939 util.SMlog("*** INTERRUPTED CLONE OP: for %s (%s)" % (origUuid, jval)) 

940 lvs = LvmCowUtil.getVolumeInfo(self.lvmCache) 

941 baseUuid, clonUuid = jval.split("_") 

942 

943 # is there a "base copy" VDI? 

944 if not lvs.get(baseUuid): 

945 # no base copy: make sure the original is there 

946 if lvs.get(origUuid): 

947 util.SMlog("*** INTERRUPTED CLONE OP: nothing to do") 

948 return 

949 raise util.SMException("base copy %s not present, " \ 

950 "but no original %s found" % (baseUuid, origUuid)) 

951 

952 vdis = LvmCowUtil.getVDIInfo(self.lvmCache) 

953 base = vdis[baseUuid] 

954 cowutil = getCowUtil(base.vdiType) 

955 

956 if forceUndo: 

957 util.SMlog("Explicit revert") 

958 self._undoCloneOp(cowutil, lvs, origUuid, baseUuid, clonUuid) 

959 return 

960 

961 if not lvs.get(origUuid) or (clonUuid and not lvs.get(clonUuid)): 

962 util.SMlog("One or both leaves missing => revert") 

963 self._undoCloneOp(cowutil, lvs, origUuid, baseUuid, clonUuid) 

964 return 

965 

966 if vdis[origUuid].scanError or (clonUuid and vdis[clonUuid].scanError): 

967 util.SMlog("One or both leaves invalid => revert") 

968 self._undoCloneOp(cowutil, lvs, origUuid, baseUuid, clonUuid) 

969 return 

970 

971 orig = vdis[origUuid] 

972 self.lvActivator.activate(baseUuid, base.lvName, False) 

973 self.lvActivator.activate(origUuid, orig.lvName, False) 

974 if orig.parentUuid != baseUuid: 

975 parent = vdis[orig.parentUuid] 

976 self.lvActivator.activate(parent.uuid, parent.lvName, False) 

977 origPath = os.path.join(self.path, orig.lvName) 

978 

979 if cowutil.check(origPath) != CowUtil.CheckResult.Success: 

980 util.SMlog("Orig image invalid => revert") 

981 self._undoCloneOp(cowutil, lvs, origUuid, baseUuid, clonUuid) 

982 return 

983 

984 if clonUuid: 

985 clon = vdis[clonUuid] 

986 clonPath = os.path.join(self.path, clon.lvName) 

987 self.lvActivator.activate(clonUuid, clon.lvName, False) 

988 if cowutil.check(clonPath) != CowUtil.CheckResult.Success: 

989 util.SMlog("Clon image invalid => revert") 

990 self._undoCloneOp(cowutil, lvs, origUuid, baseUuid, clonUuid) 

991 return 

992 

993 util.SMlog("Snapshot appears valid, will not roll back") 

994 self._completeCloneOp(cowutil, vdis, origUuid, baseUuid, clonUuid) 

995 

996 def _undoCloneOp(self, cowutil, lvs, origUuid, baseUuid, clonUuid): 

997 base = lvs[baseUuid] 

998 basePath = os.path.join(self.path, base.name) 

999 

1000 # make the parent RW 

1001 if base.readonly: 

1002 self.lvmCache.setReadonly(base.name, False) 

1003 

1004 ns = NS_PREFIX_LVM + self.uuid 

1005 origRefcountBinary = RefCounter.check(origUuid, ns)[1] 

1006 origRefcountNormal = 0 

1007 

1008 # un-hide the parent 

1009 if VdiType.isCowImage(base.vdiType): 

1010 self.lvActivator.activate(baseUuid, base.name, False) 

1011 origRefcountNormal = 1 

1012 imageInfo = cowutil.getInfo(basePath, LvmCowUtil.extractUuid, False) 

1013 if imageInfo.hidden: 

1014 cowutil.setHidden(basePath, False) 

1015 elif base.hidden: 

1016 self.lvmCache.setHidden(base.name, False) 

1017 

1018 # remove the child nodes 

1019 if clonUuid and lvs.get(clonUuid): 

1020 if not VdiType.isCowImage(lvs[clonUuid].vdiType): 

1021 raise util.SMException("clone %s not a COW image" % clonUuid) 

1022 self.lvmCache.remove(lvs[clonUuid].name) 

1023 if self.lvActivator.get(clonUuid, False): 

1024 self.lvActivator.remove(clonUuid, False) 

1025 if lvs.get(origUuid): 

1026 self.lvmCache.remove(lvs[origUuid].name) 

1027 

1028 # inflate the parent to fully-allocated size 

1029 if VdiType.isCowImage(base.vdiType): 

1030 lvmcowutil = LvmCowUtil(cowutil) 

1031 fullSize = lvmcowutil.calcVolumeSize(imageInfo.sizeVirt) 

1032 lvmcowutil.inflate(self.journaler, self.uuid, baseUuid, base.vdiType, fullSize) 

1033 

1034 # rename back 

1035 origLV = LV_PREFIX[base.vdiType] + origUuid 

1036 self.lvmCache.rename(base.name, origLV) 

1037 RefCounter.reset(baseUuid, ns) 

1038 if self.lvActivator.get(baseUuid, False): 

1039 self.lvActivator.replace(baseUuid, origUuid, origLV, False) 

1040 RefCounter.set(origUuid, origRefcountNormal, origRefcountBinary, ns) 

1041 

1042 # At this stage, tapdisk and SM vdi will be in paused state. Remove 

1043 # flag to facilitate vm deactivate 

1044 origVdiRef = self.session.xenapi.VDI.get_by_uuid(origUuid) 

1045 self.session.xenapi.VDI.remove_from_sm_config(origVdiRef, 'paused') 

1046 

1047 # update LVM metadata on slaves 

1048 slaves = util.get_slaves_attached_on(self.session, [origUuid]) 

1049 LvmCowUtil.refreshVolumeOnSlaves(self.session, self.uuid, self.vgname, 

1050 origLV, origUuid, slaves) 

1051 

1052 util.SMlog("*** INTERRUPTED CLONE OP: rollback success") 

1053 

1054 def _completeCloneOp(self, cowutil, vdis, origUuid, baseUuid, clonUuid): 

1055 """Finalize the interrupted snapshot/clone operation. This must not be 

1056 called from the live snapshot op context because we attempt to pause/ 

1057 unpause the VBD here (the VBD is already paused during snapshot, so it 

1058 would cause a deadlock)""" 

1059 base = vdis[baseUuid] 

1060 clon = None 

1061 if clonUuid: 

1062 clon = vdis[clonUuid] 

1063 

1064 cleanup.abort(self.uuid) 

1065 

1066 # make sure the parent is hidden and read-only 

1067 if not base.hidden: 

1068 if not VdiType.isCowImage(base.vdiType): 

1069 self.lvmCache.setHidden(base.lvName) 

1070 else: 

1071 basePath = os.path.join(self.path, base.lvName) 

1072 cowutil.setHidden(basePath) 

1073 if not base.lvReadonly: 

1074 self.lvmCache.setReadonly(base.lvName, True) 

1075 

1076 # NB: since this snapshot-preserving call is only invoked outside the 

1077 # snapshot op context, we assume the LVM metadata on the involved slave 

1078 # has by now been refreshed and do not attempt to do it here 

1079 

1080 # Update the original record 

1081 try: 

1082 vdi_ref = self.session.xenapi.VDI.get_by_uuid(origUuid) 

1083 sm_config = self.session.xenapi.VDI.get_sm_config(vdi_ref) 

1084 type = self.session.xenapi.VDI.get_type(vdi_ref) 

1085 sm_config["vdi_type"] = vdis[origUuid].vdiType 

1086 sm_config['vhd-parent'] = baseUuid 

1087 self.session.xenapi.VDI.set_sm_config(vdi_ref, sm_config) 

1088 except XenAPI.Failure: 

1089 util.SMlog("ERROR updating the orig record") 

1090 

1091 # introduce the new VDI records 

1092 if clonUuid: 

1093 try: 

1094 clon_vdi = VDI.VDI(self, clonUuid) 

1095 clon_vdi.read_only = False 

1096 clon_vdi.location = clonUuid 

1097 clon_vdi.utilisation = clon.sizeLV 

1098 clon_vdi.sm_config = { 

1099 "vdi_type": clon.vdiType, 

1100 "vhd-parent": baseUuid} 

1101 

1102 if not self.legacyMode: 

1103 LVMMetadataHandler(self.mdpath). \ 

1104 ensureSpaceIsAvailableForVdis(1) 

1105 

1106 clon_vdi_ref = clon_vdi._db_introduce() 

1107 util.SMlog("introduced clon VDI: %s (%s)" % \ 

1108 (clon_vdi_ref, clonUuid)) 

1109 

1110 vdi_info = {UUID_TAG: clonUuid, 

1111 NAME_LABEL_TAG: clon_vdi.label, 

1112 NAME_DESCRIPTION_TAG: clon_vdi.description, 

1113 IS_A_SNAPSHOT_TAG: 0, 

1114 SNAPSHOT_OF_TAG: '', 

1115 SNAPSHOT_TIME_TAG: '', 

1116 TYPE_TAG: type, 

1117 VDI_TYPE_TAG: clon_vdi.sm_config['vdi_type'], 

1118 READ_ONLY_TAG: int(clon_vdi.read_only), 

1119 MANAGED_TAG: int(clon_vdi.managed), 

1120 METADATA_OF_POOL_TAG: '' 

1121 } 

1122 

1123 if not self.legacyMode: 

1124 LVMMetadataHandler(self.mdpath).addVdi(vdi_info) 

1125 

1126 except XenAPI.Failure: 

1127 util.SMlog("ERROR introducing the clon record") 

1128 

1129 try: 

1130 base_vdi = VDI.VDI(self, baseUuid) # readonly parent 

1131 base_vdi.label = "base copy" 

1132 base_vdi.read_only = True 

1133 base_vdi.location = baseUuid 

1134 base_vdi.size = base.sizeVirt 

1135 base_vdi.utilisation = base.sizeLV 

1136 base_vdi.managed = False 

1137 base_vdi.sm_config = { 

1138 "vdi_type": base.vdiType, 

1139 "vhd-parent": baseUuid} 

1140 

1141 if not self.legacyMode: 

1142 LVMMetadataHandler(self.mdpath).ensureSpaceIsAvailableForVdis(1) 

1143 

1144 base_vdi_ref = base_vdi._db_introduce() 

1145 util.SMlog("introduced base VDI: %s (%s)" % \ 

1146 (base_vdi_ref, baseUuid)) 

1147 

1148 vdi_info = {UUID_TAG: baseUuid, 

1149 NAME_LABEL_TAG: base_vdi.label, 

1150 NAME_DESCRIPTION_TAG: base_vdi.description, 

1151 IS_A_SNAPSHOT_TAG: 0, 

1152 SNAPSHOT_OF_TAG: '', 

1153 SNAPSHOT_TIME_TAG: '', 

1154 TYPE_TAG: type, 

1155 VDI_TYPE_TAG: base_vdi.sm_config['vdi_type'], 

1156 READ_ONLY_TAG: int(base_vdi.read_only), 

1157 MANAGED_TAG: int(base_vdi.managed), 

1158 METADATA_OF_POOL_TAG: '' 

1159 } 

1160 

1161 if not self.legacyMode: 

1162 LVMMetadataHandler(self.mdpath).addVdi(vdi_info) 

1163 except XenAPI.Failure: 

1164 util.SMlog("ERROR introducing the base record") 

1165 

1166 util.SMlog("*** INTERRUPTED CLONE OP: complete") 

1167 

1168 def _undoAllJournals(self): 

1169 """Undo all COW image & SM interrupted journaled operations. This call must 

1170 be serialized with respect to all operations that create journals""" 

1171 # undoing interrupted inflates must be done first, since undoing COW images 

1172 # ops might require inflations 

1173 self.lock.acquire() 

1174 try: 

1175 self._undoAllInflateJournals() 

1176 self._undoAllCowJournals() 

1177 self._handleInterruptedCloneOps() 

1178 self._handleInterruptedCoalesceLeaf() 

1179 finally: 

1180 self.lock.release() 

1181 self.cleanup() 

1182 

1183 def _undoAllInflateJournals(self): 

1184 entries = self.journaler.getAll(LvmCowUtil.JOURNAL_INFLATE) 

1185 if len(entries) == 0: 

1186 return 

1187 self._loadvdis() 

1188 for uuid, val in entries.items(): 

1189 vdi = self.vdis.get(uuid) 

1190 if vdi: 1190 ↛ 1210line 1190 didn't jump to line 1210, because the condition on line 1190 was never false

1191 util.SMlog("Found inflate journal %s, deflating %s to %s" % \ 

1192 (uuid, vdi.path, val)) 

1193 if vdi.readonly: 1193 ↛ 1194line 1193 didn't jump to line 1194, because the condition on line 1193 was never true

1194 self.lvmCache.setReadonly(vdi.lvname, False) 

1195 self.lvActivator.activate(uuid, vdi.lvname, False) 

1196 currSizeLV = self.lvmCache.getSize(vdi.lvname) 

1197 

1198 cowutil = getCowUtil(vdi.vdi_type) 

1199 lvmcowutil = LvmCowUtil(cowutil) 

1200 

1201 footer_size = cowutil.getFooterSize() 

1202 util.zeroOut(vdi.path, currSizeLV - footer_size, footer_size) 

1203 lvmcowutil.deflate(self.lvmCache, vdi.lvname, int(val)) 

1204 if vdi.readonly: 1204 ↛ 1205line 1204 didn't jump to line 1205, because the condition on line 1204 was never true

1205 self.lvmCache.setReadonly(vdi.lvname, True) 

1206 if "true" == self.session.xenapi.SR.get_shared(self.sr_ref): 1206 ↛ 1207line 1206 didn't jump to line 1207, because the condition on line 1206 was never true

1207 LvmCowUtil.refreshVolumeOnAllSlaves( 

1208 self.session, self.uuid, self.vgname, vdi.lvname, uuid 

1209 ) 

1210 self.journaler.remove(LvmCowUtil.JOURNAL_INFLATE, uuid) 

1211 delattr(self, "vdiInfo") 

1212 delattr(self, "allVDIs") 

1213 

1214 def _undoAllCowJournals(self): 

1215 """ 

1216 Check if there are COW journals in existence and revert them. 

1217 """ 

1218 journals = LvmCowUtil.getAllResizeJournals(self.lvmCache) 

1219 if len(journals) == 0: 1219 ↛ 1221line 1219 didn't jump to line 1221, because the condition on line 1219 was never false

1220 return 

1221 self._loadvdis() 

1222 

1223 for uuid, jlvName in journals: 

1224 vdi = self.vdis[uuid] 

1225 util.SMlog("Found COW journal %s, reverting %s" % (uuid, vdi.path)) 

1226 cowutil = getCowUtil(vdi.vdi_type) 

1227 lvmcowutil = LvmCowUtil(cowutil) 

1228 

1229 self.lvActivator.activate(uuid, vdi.lvname, False) 

1230 self.lvmCache.activateNoRefcount(jlvName) 

1231 fullSize = lvmcowutil.calcVolumeSize(vdi.size) 

1232 lvmcowutil.inflate(self.journaler, self.uuid, vdi.uuid, vdi.vdi_type, fullSize) 

1233 try: 

1234 jFile = os.path.join(self.path, jlvName) 

1235 cowutil.revert(vdi.path, jFile) 

1236 except util.CommandException: 

1237 util.logException("COW journal revert") 

1238 cowutil.check(vdi.path) 

1239 util.SMlog("COW image revert failed but COW image ok: removing journal") 

1240 # Attempt to reclaim unused space 

1241 

1242 

1243 imageInfo = cowutil.getInfo(vdi.path, LvmCowUtil.extractUuid, False) 

1244 NewSize = lvmcowutil.calcVolumeSize(imageInfo.sizeVirt) 

1245 if NewSize < fullSize: 

1246 lvmcowutil.deflate(self.lvmCache, vdi.lvname, int(NewSize)) 

1247 LvmCowUtil.refreshVolumeOnAllSlaves(self.session, self.uuid, self.vgname, vdi.lvname, uuid) 

1248 self.lvmCache.remove(jlvName) 

1249 delattr(self, "vdiInfo") 

1250 delattr(self, "allVDIs") 

1251 

1252 def call_on_slave(self, args, host_refs, message: str): 

1253 master_ref = util.get_this_host_ref(self.session) 

1254 for hostRef in host_refs: 

1255 if hostRef == master_ref: 1255 ↛ 1256line 1255 didn't jump to line 1256, because the condition on line 1255 was never true

1256 continue 

1257 util.SMlog(f"{message} on slave {hostRef}") 

1258 rv = self.session.xenapi.host.call_plugin( 

1259 hostRef, self.PLUGIN_ON_SLAVE, "multi", args) 

1260 util.SMlog("call-plugin returned: %s" % rv) 

1261 if not rv: 1261 ↛ 1262line 1261 didn't jump to line 1262, because the condition on line 1261 was never true

1262 raise Exception('plugin %s failed' % self.PLUGIN_ON_SLAVE) 

1263 

1264 def _updateSlavesOnClone(self, hostRefs, origOldLV, origLV, 

1265 baseUuid, baseLV): 

1266 """We need to reactivate the original LV on each slave (note that the 

1267 name for the original LV might change), as well as init the refcount 

1268 for the base LV""" 

1269 args = {"vgName": self.vgname, 

1270 "action1": "refresh", 

1271 "lvName1": origLV, 

1272 "action2": "activate", 

1273 "ns2": NS_PREFIX_LVM + self.uuid, 

1274 "lvName2": baseLV, 

1275 "uuid2": baseUuid} 

1276 

1277 message = f"Updating {origOldLV}, {origLV}, {baseLV}" 

1278 self.call_on_slave(args, hostRefs, message) 

1279 

1280 def _updateSlavesOnCBTClone(self, hostRefs, cbtlog): 

1281 """Reactivate and refresh CBT log file on slaves""" 

1282 args = {"vgName": self.vgname, 

1283 "action1": "deactivateNoRefcount", 

1284 "lvName1": cbtlog, 

1285 "action2": "refresh", 

1286 "lvName2": cbtlog} 

1287 

1288 message = f"Updating {cbtlog}" 

1289 self.call_on_slave(args, hostRefs, message) 

1290 

1291 def _updateSlavesOnRemove(self, hostRefs, baseUuid, baseLV): 

1292 """Tell the slave we deleted the base image""" 

1293 args = {"vgName": self.vgname, 

1294 "action1": "cleanupLockAndRefcount", 

1295 "uuid1": baseUuid, 

1296 "ns1": NS_PREFIX_LVM + self.uuid} 

1297 

1298 message = f"Cleaning locks for {baseLV}" 

1299 self.call_on_slave(args, hostRefs, message) 

1300 

1301 def _deactivateOnSlave(self, hostRefs, lvname): 

1302 """Tell the slave we need to deactivate the base image""" 

1303 args = { 

1304 "vgName": self.vgname, 

1305 "action1": "deactivateNoRefcount", 

1306 "lvName1": lvname} 

1307 

1308 message = f"Deactivating {lvname}" 

1309 self.call_on_slave(args, hostRefs, message) 

1310 

1311 def _cleanup(self, skipLockCleanup=False): 

1312 """delete stale refcounter, flag, and lock files""" 

1313 RefCounter.resetAll(NS_PREFIX_LVM + self.uuid) 

1314 IPCFlag(self.uuid).clearAll() 

1315 if not skipLockCleanup: 1315 ↛ 1316line 1315 didn't jump to line 1316, because the condition on line 1315 was never true

1316 lock.Lock.cleanupAll(self.uuid) 

1317 lock.Lock.cleanupAll(NS_PREFIX_LVM + self.uuid) 

1318 

1319 def _prepareTestMode(self): 

1320 util.SMlog("Test mode: %s" % self.testMode) 

1321 if self.ENV_VAR_VHD_TEST.get(self.testMode): 1321 ↛ 1322line 1321 didn't jump to line 1322, because the condition on line 1321 was never true

1322 os.environ[self.ENV_VAR_VHD_TEST[self.testMode]] = "yes" 

1323 util.SMlog("Setting env %s" % self.ENV_VAR_VHD_TEST[self.testMode]) 

1324 

1325 def _kickGC(self): 

1326 util.SMlog("Kicking GC") 

1327 cleanup.start_gc_service(self.uuid) 

1328 

1329 def ensureCBTSpace(self, virtual_size=0): 

1330 # Ensure we have space for at least one LV 

1331 size = max(util.roundup(CBT_BLOCK_SIZE, virtual_size//CBT_BLOCK_SIZE), self.journaler.LV_SIZE) 

1332 self._ensureSpaceAvailable(size) 

1333 

1334 

1335class LVMVDI(VDI.VDI): 

1336 

1337 JRN_CLONE = "clone" # journal entry type for the clone operation 

1338 

1339 @override 

1340 def load(self, vdi_uuid) -> None: 

1341 self.lock = self.sr.lock 

1342 self.lvActivator = self.sr.lvActivator 

1343 self.loaded = False 

1344 if self.sr.legacyMode or util.fistpoint.is_active("xenrt_default_vdi_type_legacy"): 1344 ↛ 1346line 1344 didn't jump to line 1346, because the condition on line 1344 was never false

1345 self._setType(VdiType.RAW) 

1346 self.uuid = vdi_uuid 

1347 self.location = self.uuid 

1348 self.exists = True 

1349 

1350 if hasattr(self.sr, "vdiInfo") and self.sr.vdiInfo.get(self.uuid): 

1351 self._initFromVDIInfo(self.sr.vdiInfo[self.uuid]) 

1352 if self.parent: 1352 ↛ 1353line 1352 didn't jump to line 1353, because the condition on line 1352 was never true

1353 self.sm_config_override['vhd-parent'] = self.parent 

1354 else: 

1355 self.sm_config_override['vhd-parent'] = None 

1356 return 

1357 

1358 # scan() didn't run: determine the type of the VDI manually 

1359 if self._determineType(): 1359 ↛ 1362line 1359 didn't jump to line 1362, because the condition on line 1359 was never false

1360 return 

1361 

1362 image_format = None 

1363 

1364 # the VDI must be in the process of being created 

1365 self.exists = False 

1366 

1367 vdi_sm_config = self.sr.srcmd.params.get("vdi_sm_config") 

1368 if vdi_sm_config: 

1369 image_format = self.sr.read_config_image_format(vdi_sm_config) 

1370 

1371 if not image_format: 

1372 if self.sr.srcmd.cmd == "vdi_create": 

1373 size = int(self.sr.srcmd.params['args'][0]) 

1374 # In the case of vdi_create, the first parameter is size. 

1375 # We need it to validate the vdi_type choice 

1376 for image_format in self.sr.preferred_image_formats: 

1377 vdi_type = self.sr._resolve_vdi_type_from_image_format(image_format) 

1378 cowutil = getCowUtil(vdi_type) 

1379 try: 

1380 cowutil.validateAndRoundImageSize(size) 

1381 break 

1382 except xs_errors.SROSError: 

1383 util.SMlog(f"We won't be able to create the VDI with format {vdi_type}.") 

1384 else: 

1385 # For cbt_metadata, we found ourselves here without being a vdi_create, we don't have size 

1386 util.SMlog("Not a vdi_create, must be cbtlog") 

1387 image_format = self.sr.preferred_image_formats[0] 

1388 self._setType(self.sr._resolve_vdi_type_from_image_format(image_format)) 

1389 

1390 if self.sr.legacyMode and self.sr.cmd == 'vdi_create' and VdiType.isCowImage(self.vdi_type): 

1391 raise xs_errors.XenError('VDICreate', opterr='Cannot create COW type disk in legacy mode') 

1392 

1393 self.lvname = "%s%s" % (LV_PREFIX[self.vdi_type], vdi_uuid) 

1394 self.path = os.path.join(self.sr.path, self.lvname) 

1395 

1396 @override 

1397 def create(self, sr_uuid, vdi_uuid, size) -> str: 

1398 util.SMlog("LVMVDI.create for %s" % self.uuid) 

1399 if not self.sr.isMaster: 

1400 raise xs_errors.XenError('LVMMaster') 

1401 if self.exists: 

1402 raise xs_errors.XenError('VDIExists') 

1403 

1404 size = self.cowutil.validateAndRoundImageSize(int(size)) 

1405 

1406 util.SMlog("LVMVDI.create: type = %s, %s (size=%s)" % \ 

1407 (self.vdi_type, self.path, size)) 

1408 lvSize = 0 

1409 self.sm_config = self.sr.srcmd.params["vdi_sm_config"] 

1410 if not VdiType.isCowImage(self.vdi_type): 

1411 lvSize = util.roundup(lvutil.LVM_SIZE_INCREMENT, int(size)) 

1412 else: 

1413 if self.sr.provision == "thin": 

1414 lvSize = util.roundup( 

1415 lvutil.LVM_SIZE_INCREMENT, 

1416 self.cowutil.calcOverheadEmpty(max(size, self.cowutil.getDefaultPreallocationSizeVirt())) 

1417 ) 

1418 elif self.sr.provision == "thick": 

1419 lvSize = self.lvmcowutil.calcVolumeSize(int(size)) 

1420 

1421 self.sr._ensureSpaceAvailable(lvSize) 

1422 

1423 try: 

1424 self.sr.lvmCache.create(self.lvname, lvSize) 

1425 if not VdiType.isCowImage(self.vdi_type): 

1426 self.size = self.sr.lvmCache.getSize(self.lvname) 

1427 else: 

1428 self.cowutil.create( 

1429 self.path, int(size), False, self.cowutil.getDefaultPreallocationSizeVirt() 

1430 ) 

1431 self.size = self.cowutil.getSizeVirt(self.path) 

1432 self.sr.lvmCache.deactivateNoRefcount(self.lvname) 

1433 except util.CommandException as e: 

1434 util.SMlog("Unable to create VDI") 

1435 self.sr.lvmCache.remove(self.lvname) 

1436 raise xs_errors.XenError('VDICreate', opterr='error %d' % e.code) 

1437 

1438 self.utilisation = lvSize 

1439 self.sm_config["vdi_type"] = self.vdi_type 

1440 self.sm_config["image-format"] = getImageStringFromVdiType(self.vdi_type) 

1441 

1442 if not self.sr.legacyMode: 

1443 LVMMetadataHandler(self.sr.mdpath).ensureSpaceIsAvailableForVdis(1) 

1444 

1445 self.ref = self._db_introduce() 

1446 self.sr._updateStats(self.sr.uuid, self.size) 

1447 

1448 vdi_info = {UUID_TAG: self.uuid, 

1449 NAME_LABEL_TAG: util.to_plain_string(self.label), 

1450 NAME_DESCRIPTION_TAG: util.to_plain_string(self.description), 

1451 IS_A_SNAPSHOT_TAG: 0, 

1452 SNAPSHOT_OF_TAG: '', 

1453 SNAPSHOT_TIME_TAG: '', 

1454 TYPE_TAG: self.ty, 

1455 VDI_TYPE_TAG: self.vdi_type, 

1456 READ_ONLY_TAG: int(self.read_only), 

1457 MANAGED_TAG: int(self.managed), 

1458 METADATA_OF_POOL_TAG: '' 

1459 } 

1460 

1461 if not self.sr.legacyMode: 

1462 LVMMetadataHandler(self.sr.mdpath).addVdi(vdi_info) 

1463 

1464 return VDI.VDI.get_params(self) 

1465 

1466 @override 

1467 def delete(self, sr_uuid, vdi_uuid, data_only=False) -> None: 

1468 util.SMlog("LVMVDI.delete for %s" % self.uuid) 

1469 try: 

1470 self._loadThis() 

1471 except xs_errors.SRException as e: 

1472 # Catch 'VDI doesn't exist' exception 

1473 if e.errno == 46: 

1474 return super(LVMVDI, self).delete(sr_uuid, vdi_uuid, data_only) 

1475 raise 

1476 

1477 vdi_ref = self.sr.srcmd.params['vdi_ref'] 

1478 if not self.session.xenapi.VDI.get_managed(vdi_ref): 

1479 raise xs_errors.XenError("VDIDelete", \ 

1480 opterr="Deleting non-leaf node not permitted") 

1481 

1482 if not self.hidden: 

1483 self._markHidden() 

1484 

1485 if not data_only: 

1486 # Remove from XAPI and delete from MGT 

1487 self._db_forget() 

1488 else: 

1489 # If this is a data_destroy call, don't remove from XAPI db 

1490 # Only delete from MGT 

1491 if not self.sr.legacyMode: 

1492 LVMMetadataHandler(self.sr.mdpath).deleteVdiFromMetadata(self.uuid) 

1493 

1494 # deactivate here because it might be too late to do it in the "final" 

1495 # step: GC might have removed the LV by then 

1496 if self.sr.lvActivator.get(self.uuid, False): 

1497 self.sr.lvActivator.deactivate(self.uuid, False) 

1498 

1499 try: 

1500 self.sr.lvmCache.remove(self.lvname) 

1501 self.sr.lock.cleanup(vdi_uuid, NS_PREFIX_LVM + sr_uuid) 

1502 self.sr.lock.cleanupAll(vdi_uuid) 

1503 except xs_errors.SRException as e: 

1504 util.SMlog( 

1505 "Failed to remove the volume (maybe is leaf coalescing) " 

1506 "for %s err:%d" % (self.uuid, e.errno)) 

1507 

1508 self.sr._updateStats(self.sr.uuid, -self.size) 

1509 self.sr._kickGC() 

1510 return super(LVMVDI, self).delete(sr_uuid, vdi_uuid, data_only) 

1511 

1512 @override 

1513 def attach(self, sr_uuid, vdi_uuid) -> str: 

1514 util.SMlog("LVMVDI.attach for %s" % self.uuid) 

1515 if self.sr.journaler.hasJournals(self.uuid): 

1516 raise xs_errors.XenError('VDIUnavailable', 

1517 opterr='Interrupted operation detected on this VDI, ' 

1518 'scan SR first to trigger auto-repair') 

1519 

1520 writable = ('args' not in self.sr.srcmd.params) or \ 

1521 (self.sr.srcmd.params['args'][0] == "true") 

1522 needInflate = True 

1523 if not VdiType.isCowImage(self.vdi_type) or not writable: 

1524 needInflate = False 

1525 else: 

1526 self._loadThis() 

1527 if self.utilisation >= self.lvmcowutil.calcVolumeSize(self.size): 

1528 needInflate = False 

1529 

1530 if needInflate: 

1531 try: 

1532 self._prepareThin(True, self.vdi_type) 

1533 except: 

1534 util.logException("attach") 

1535 raise xs_errors.XenError('LVMProvisionAttach') 

1536 

1537 if self.cowutil.isCoalesceableOnRemote(): 

1538 # We make the chain RW before it's used so it can be coalesced online 

1539 self._make_chain_rw() 

1540 

1541 try: 

1542 return self._attach() 

1543 finally: 

1544 if not self.sr.lvActivator.deactivateAll(): 

1545 util.SMlog("Failed to deactivate LVs back (%s)" % self.uuid) 

1546 

1547 @override 

1548 def detach(self, sr_uuid, vdi_uuid) -> None: 

1549 util.SMlog("LVMVDI.detach for %s" % self.uuid) 

1550 self._loadThis() 

1551 already_deflated = (self.utilisation < \ 

1552 self.lvmcowutil.calcVolumeSize(self.size)) 

1553 needDeflate = True 

1554 if not VdiType.isCowImage(self.vdi_type) or already_deflated: 

1555 needDeflate = False 

1556 elif self.sr.provision == "thick": 

1557 needDeflate = False 

1558 # except for snapshots, which are always deflated 

1559 if self.sr.srcmd.cmd != 'vdi_detach_from_config': 

1560 vdi_ref = self.sr.srcmd.params['vdi_ref'] 

1561 snap = self.session.xenapi.VDI.get_is_a_snapshot(vdi_ref) 

1562 if snap: 

1563 needDeflate = True 

1564 

1565 if needDeflate: 

1566 try: 

1567 self._prepareThin(False, self.vdi_type) 

1568 except: 

1569 util.logException("_prepareThin") 

1570 raise xs_errors.XenError('VDIUnavailable', opterr='deflate') 

1571 

1572 try: 

1573 self._detach() 

1574 finally: 

1575 if not self.sr.lvActivator.deactivateAll(): 

1576 raise xs_errors.XenError("SMGeneral", opterr="deactivation") 

1577 

1578 # We only support offline resize 

1579 @override 

1580 def resize(self, sr_uuid, vdi_uuid, size) -> str: 

1581 util.SMlog("LVMVDI.resize for %s" % self.uuid) 

1582 if not self.sr.isMaster: 

1583 raise xs_errors.XenError('LVMMaster') 

1584 

1585 self._loadThis() 

1586 if self.hidden: 

1587 raise xs_errors.XenError('VDIUnavailable', opterr='hidden VDI') 

1588 

1589 if size < self.size: 

1590 util.SMlog('vdi_resize: shrinking not supported: ' + \ 

1591 '(current size: %d, new size: %d)' % (self.size, size)) 

1592 raise xs_errors.XenError('VDISize', opterr='shrinking not allowed') 

1593 

1594 size = self.cowutil.validateAndRoundImageSize(int(size)) 

1595 

1596 if size == self.size: 

1597 return VDI.VDI.get_params(self) 

1598 

1599 if not VdiType.isCowImage(self.vdi_type): 

1600 lvSizeOld = self.size 

1601 lvSizeNew = util.roundup(lvutil.LVM_SIZE_INCREMENT, size) 

1602 else: 

1603 lvSizeOld = self.utilisation 

1604 lvSizeNew = self.lvmcowutil.calcVolumeSize(size) 

1605 if self.sr.provision == "thin": 

1606 # VDI is currently deflated, so keep it deflated 

1607 lvSizeNew = lvSizeOld 

1608 assert(lvSizeNew >= lvSizeOld) 

1609 spaceNeeded = lvSizeNew - lvSizeOld 

1610 self.sr._ensureSpaceAvailable(spaceNeeded) 

1611 

1612 oldSize = self.size 

1613 if not VdiType.isCowImage(self.vdi_type): 

1614 self.sr.lvmCache.setSize(self.lvname, lvSizeNew) 

1615 self.size = self.sr.lvmCache.getSize(self.lvname) 

1616 self.utilisation = self.size 

1617 else: 

1618 if lvSizeNew != lvSizeOld: 

1619 self.lvmcowutil.inflate(self.sr.journaler, self.sr.uuid, self.uuid, self.vdi_type, lvSizeNew) 

1620 if self.vdi_type == VdiType.QCOW2: 

1621 # We only do this for QCOW2 since qemu-img need to read the chain to resize 

1622 self._chainSetActive(True, True) 

1623 self.cowutil.setSizeVirtFast(self.path, size) 

1624 self.size = self.cowutil.getSizeVirt(self.path) 

1625 self.utilisation = self.sr.lvmCache.getSize(self.lvname) 

1626 

1627 vdi_ref = self.sr.srcmd.params['vdi_ref'] 

1628 self.session.xenapi.VDI.set_virtual_size(vdi_ref, str(self.size)) 

1629 self.session.xenapi.VDI.set_physical_utilisation(vdi_ref, 

1630 str(self.utilisation)) 

1631 self.sr._updateStats(self.sr.uuid, self.size - oldSize) 

1632 super(LVMVDI, self).resize_cbt(self.sr.uuid, self.uuid, self.size) 

1633 if self.vdi_type == VdiType.QCOW2: 

1634 self._chainSetActive(False, True) 

1635 return VDI.VDI.get_params(self) 

1636 

1637 @override 

1638 def clone(self, sr_uuid, vdi_uuid) -> str: 

1639 return self._do_snapshot( 

1640 sr_uuid, vdi_uuid, VDI.SNAPSHOT_DOUBLE, cloneOp=True) 

1641 

1642 @override 

1643 def compose(self, sr_uuid, vdi1, vdi2) -> None: 

1644 util.SMlog("LVMSR.compose for %s -> %s" % (vdi2, vdi1)) 

1645 if not VdiType.isCowImage(self.vdi_type): 

1646 raise xs_errors.XenError('Unimplemented') 

1647 

1648 parent_uuid = vdi1 

1649 parent_lvname = LV_PREFIX[self.vdi_type] + parent_uuid 

1650 assert(self.sr.lvmCache.checkLV(parent_lvname)) 

1651 parent_path = os.path.join(self.sr.path, parent_lvname) 

1652 

1653 self.sr.lvActivator.activate(self.uuid, self.lvname, False) 

1654 self.sr.lvActivator.activate(parent_uuid, parent_lvname, False) 

1655 

1656 self.cowutil.setParent(self.path, parent_path, False) 

1657 self.cowutil.setHidden(parent_path) 

1658 self.sr.session.xenapi.VDI.set_managed(self.sr.srcmd.params['args'][0], False) 

1659 

1660 if not blktap2.VDI.tap_refresh(self.session, self.sr.uuid, self.uuid, 

1661 True): 

1662 raise util.SMException("failed to refresh VDI %s" % self.uuid) 

1663 

1664 util.SMlog("Compose done") 

1665 

1666 def reset_leaf(self, sr_uuid, vdi_uuid): 

1667 util.SMlog("LVMSR.reset_leaf for %s" % vdi_uuid) 

1668 if not VdiType.isCowImage(self.vdi_type): 

1669 raise xs_errors.XenError('Unimplemented') 

1670 

1671 self.sr.lvActivator.activate(self.uuid, self.lvname, False) 

1672 

1673 # safety check 

1674 if not self.cowutil.hasParent(self.path): 

1675 raise util.SMException("ERROR: VDI %s has no parent, " + \ 

1676 "will not reset contents" % self.uuid) 

1677 

1678 self.cowutil.killData(self.path) 

1679 

1680 def _attach(self): 

1681 self._chainSetActive(True, True, True) 

1682 if not util.pathexists(self.path): 

1683 raise xs_errors.XenError('VDIUnavailable', \ 

1684 opterr='Could not find: %s' % self.path) 

1685 

1686 if not hasattr(self, 'xenstore_data'): 

1687 self.xenstore_data = {} 

1688 

1689 self.xenstore_data.update(scsiutil.update_XS_SCSIdata(self.uuid, \ 

1690 scsiutil.gen_synthetic_page_data(self.uuid))) 

1691 

1692 self.xenstore_data['storage-type'] = 'lvm' 

1693 self.xenstore_data['vdi-type'] = self.vdi_type 

1694 

1695 self.attached = True 

1696 self.sr.lvActivator.persist() 

1697 return VDI.VDI.attach(self, self.sr.uuid, self.uuid) 

1698 

1699 def _detach(self): 

1700 self._chainSetActive(False, True) 

1701 self.attached = False 

1702 

1703 @override 

1704 def _do_snapshot(self, sr_uuid, vdi_uuid, snapType, 

1705 cloneOp=False, secondary=None, cbtlog=None, is_mirror_destination=False) -> str: 

1706 # If cbt enabled, save file consistency state 

1707 if cbtlog is not None: 

1708 if blktap2.VDI.tap_status(self.session, vdi_uuid): 1708 ↛ 1709line 1708 didn't jump to line 1709, because the condition on line 1708 was never true

1709 consistency_state = False 

1710 else: 

1711 consistency_state = True 

1712 util.SMlog("Saving log consistency state of %s for vdi: %s" % 

1713 (consistency_state, vdi_uuid)) 

1714 else: 

1715 consistency_state = None 

1716 

1717 pause_time = time.time() 

1718 if not blktap2.VDI.tap_pause(self.session, sr_uuid, vdi_uuid): 1718 ↛ 1719line 1718 didn't jump to line 1719, because the condition on line 1718 was never true

1719 raise util.SMException("failed to pause VDI %s" % vdi_uuid) 

1720 

1721 snapResult = None 

1722 try: 

1723 snapResult = self._snapshot(snapType, cloneOp, cbtlog, consistency_state, is_mirror_destination) 

1724 except Exception as e1: 

1725 try: 

1726 blktap2.VDI.tap_unpause(self.session, sr_uuid, vdi_uuid, 

1727 secondary=None) 

1728 except Exception as e2: 

1729 util.SMlog('WARNING: failed to clean up failed snapshot: ' 

1730 '%s (error ignored)' % e2) 

1731 raise 

1732 self.disable_leaf_on_secondary(vdi_uuid, secondary=secondary) 

1733 blktap2.VDI.tap_unpause(self.session, sr_uuid, vdi_uuid, secondary) 

1734 unpause_time = time.time() 

1735 if (unpause_time - pause_time) > LONG_SNAPTIME: 1735 ↛ 1736line 1735 didn't jump to line 1736, because the condition on line 1735 was never true

1736 util.SMlog('WARNING: snapshot paused VM for %s seconds' % 

1737 (unpause_time - pause_time)) 

1738 return snapResult 

1739 

1740 def _snapshot(self, snapType, cloneOp=False, cbtlog=None, cbt_consistency=None, is_mirror_destination=False): 

1741 util.SMlog("LVMVDI._snapshot for %s (type %s)" % (self.uuid, snapType)) 

1742 

1743 if not self.sr.isMaster: 1743 ↛ 1744line 1743 didn't jump to line 1744, because the condition on line 1743 was never true

1744 raise xs_errors.XenError('LVMMaster') 

1745 if self.sr.legacyMode: 1745 ↛ 1746line 1745 didn't jump to line 1746, because the condition on line 1745 was never true

1746 raise xs_errors.XenError('Unimplemented', opterr='In legacy mode') 

1747 

1748 self._loadThis() 

1749 if self.hidden: 1749 ↛ 1750line 1749 didn't jump to line 1750, because the condition on line 1749 was never true

1750 raise xs_errors.XenError('VDISnapshot', opterr='hidden VDI') 

1751 

1752 snapVdiType = self.sr._get_snap_vdi_type(self.vdi_type, self.size) 

1753 

1754 self.sm_config = self.session.xenapi.VDI.get_sm_config( \ 

1755 self.sr.srcmd.params['vdi_ref']) 

1756 if "type" in self.sm_config and self.sm_config['type'] == 'raw': 1756 ↛ 1757line 1756 didn't jump to line 1757, because the condition on line 1756 was never true

1757 if not util.fistpoint.is_active("testsm_clone_allow_raw"): 

1758 raise xs_errors.XenError('Unimplemented', \ 

1759 opterr='Raw VDI, snapshot or clone not permitted') 

1760 

1761 # we must activate the entire image chain because the real parent could 

1762 # theoretically be anywhere in the chain if all images under it are empty 

1763 self._chainSetActive(True, False) 

1764 if not util.pathexists(self.path): 1764 ↛ 1765line 1764 didn't jump to line 1765, because the condition on line 1764 was never true

1765 raise xs_errors.XenError('VDIUnavailable', \ 

1766 opterr='VDI unavailable: %s' % (self.path)) 

1767 

1768 if VdiType.isCowImage(self.vdi_type): 1768 ↛ 1776line 1768 didn't jump to line 1776, because the condition on line 1768 was never false

1769 depth = self.cowutil.getDepth(self.path) 

1770 if depth == -1: 1770 ↛ 1771line 1770 didn't jump to line 1771, because the condition on line 1770 was never true

1771 raise xs_errors.XenError('VDIUnavailable', \ 

1772 opterr='failed to get COW depth') 

1773 elif depth >= self.cowutil.getMaxChainLength(): 1773 ↛ 1774line 1773 didn't jump to line 1774, because the condition on line 1773 was never true

1774 raise xs_errors.XenError('SnapshotChainTooLong') 

1775 

1776 self.issnap = self.session.xenapi.VDI.get_is_a_snapshot( \ 

1777 self.sr.srcmd.params['vdi_ref']) 

1778 

1779 fullpr = self.lvmcowutil.calcVolumeSize(self.size) 

1780 thinpr = util.roundup( 

1781 lvutil.LVM_SIZE_INCREMENT, 

1782 self.cowutil.calcOverheadEmpty(max(self.size, self.cowutil.getDefaultPreallocationSizeVirt())) 

1783 ) 

1784 lvSizeOrig = thinpr 

1785 lvSizeClon = thinpr 

1786 

1787 hostRefs = [] 

1788 if self.sr.cmd == "vdi_snapshot": 

1789 hostRefs = util.get_hosts_attached_on(self.session, [self.uuid]) 

1790 if hostRefs: 1790 ↛ 1792line 1790 didn't jump to line 1792, because the condition on line 1790 was never false

1791 lvSizeOrig = fullpr 

1792 if self.sr.provision == "thick": 1792 ↛ 1798line 1792 didn't jump to line 1798, because the condition on line 1792 was never false

1793 if not self.issnap: 1793 ↛ 1794line 1793 didn't jump to line 1794, because the condition on line 1793 was never true

1794 lvSizeOrig = fullpr 

1795 if self.sr.cmd != "vdi_snapshot": 

1796 lvSizeClon = fullpr 

1797 

1798 if (snapType == VDI.SNAPSHOT_SINGLE or 1798 ↛ 1800line 1798 didn't jump to line 1800, because the condition on line 1798 was never true

1799 snapType == VDI.SNAPSHOT_INTERNAL): 

1800 lvSizeClon = 0 

1801 

1802 # the space required must include 2 journal LVs: a clone journal and an 

1803 # inflate journal (for the failure handling 

1804 size_req = lvSizeOrig + lvSizeClon + 2 * self.sr.journaler.LV_SIZE 

1805 lvSizeBase = self.size 

1806 if VdiType.isCowImage(self.vdi_type): 1806 ↛ 1809line 1806 didn't jump to line 1809, because the condition on line 1806 was never false

1807 lvSizeBase = util.roundup(lvutil.LVM_SIZE_INCREMENT, self.cowutil.getSizePhys(self.path)) 

1808 size_req -= (self.utilisation - lvSizeBase) 

1809 self.sr._ensureSpaceAvailable(size_req) 

1810 

1811 if hostRefs: 

1812 self.sr._deactivateOnSlave(hostRefs, self.lvname) 

1813 

1814 baseUuid = util.gen_uuid() 

1815 origUuid = self.uuid 

1816 clonUuid = "" 

1817 if snapType == VDI.SNAPSHOT_DOUBLE: 1817 ↛ 1819line 1817 didn't jump to line 1819, because the condition on line 1817 was never false

1818 clonUuid = util.gen_uuid() 

1819 jval = "%s_%s" % (baseUuid, clonUuid) 

1820 self.sr.journaler.create(self.JRN_CLONE, origUuid, jval) 

1821 util.fistpoint.activate("LVHDRT_clone_vdi_after_create_journal", self.sr.uuid) 

1822 

1823 try: 

1824 # self becomes the "base vdi" 

1825 origOldLV = self.lvname 

1826 baseLV = LV_PREFIX[self.vdi_type] + baseUuid 

1827 self.sr.lvmCache.rename(self.lvname, baseLV) 

1828 self.sr.lvActivator.replace(self.uuid, baseUuid, baseLV, False) 

1829 RefCounter.set(baseUuid, 1, 0, NS_PREFIX_LVM + self.sr.uuid) 

1830 self.uuid = baseUuid 

1831 self.lvname = baseLV 

1832 self.path = os.path.join(self.sr.path, baseLV) 

1833 self.label = "base copy" 

1834 self.read_only = True 

1835 self.location = self.uuid 

1836 self.managed = False 

1837 

1838 # shrink the base copy to the minimum - we do it before creating 

1839 # the snapshot volumes to avoid requiring double the space 

1840 if VdiType.isCowImage(self.vdi_type): 1840 ↛ 1843line 1840 didn't jump to line 1843, because the condition on line 1840 was never false

1841 self.lvmcowutil.deflate(self.sr.lvmCache, self.lvname, lvSizeBase) 

1842 self.utilisation = lvSizeBase 

1843 util.fistpoint.activate("LVHDRT_clone_vdi_after_shrink_parent", self.sr.uuid) 

1844 

1845 snapVDI = self._createSnap(origUuid, snapVdiType, lvSizeOrig, False, is_mirror_destination) 

1846 util.fistpoint.activate("LVHDRT_clone_vdi_after_first_snap", self.sr.uuid) 

1847 snapVDI2 = None 

1848 if snapType == VDI.SNAPSHOT_DOUBLE: 1848 ↛ 1854line 1848 didn't jump to line 1854, because the condition on line 1848 was never false

1849 snapVDI2 = self._createSnap(clonUuid, snapVdiType, lvSizeClon, True) 

1850 # If we have CBT enabled on the VDI, 

1851 # set CBT status for the new snapshot disk 

1852 if cbtlog: 

1853 snapVDI2.cbt_enabled = True 

1854 util.fistpoint.activate("LVHDRT_clone_vdi_after_second_snap", self.sr.uuid) 

1855 

1856 # note: it is important to mark the parent hidden only AFTER the 

1857 # new image children have been created, which are referencing it; 

1858 # otherwise we would introduce a race with GC that could reclaim 

1859 # the parent before we snapshot it 

1860 if not VdiType.isCowImage(self.vdi_type): 1860 ↛ 1861line 1860 didn't jump to line 1861, because the condition on line 1860 was never true

1861 self.sr.lvmCache.setHidden(self.lvname) 

1862 else: 

1863 self.cowutil.setHidden(self.path) 

1864 util.fistpoint.activate("LVHDRT_clone_vdi_after_parent_hidden", self.sr.uuid) 

1865 

1866 # set the base copy to ReadOnly 

1867 self.sr.lvmCache.setReadonly(self.lvname, True) 

1868 util.fistpoint.activate("LVHDRT_clone_vdi_after_parent_ro", self.sr.uuid) 

1869 

1870 if hostRefs: 

1871 self.sr._updateSlavesOnClone(hostRefs, origOldLV, 

1872 snapVDI.lvname, self.uuid, self.lvname) 

1873 

1874 # Update cbt files if user created snapshot (SNAPSHOT_DOUBLE) 

1875 if snapType == VDI.SNAPSHOT_DOUBLE and cbtlog: 

1876 snapVDI._cbt_snapshot(clonUuid, cbt_consistency) 

1877 if hostRefs: 1877 ↛ 1891line 1877 didn't jump to line 1891, because the condition on line 1877 was never false

1878 cbtlog_file = self._get_cbt_logname(snapVDI.uuid) 

1879 try: 

1880 self.sr._updateSlavesOnCBTClone(hostRefs, cbtlog_file) 

1881 except: 

1882 alert_name = "VDI_CBT_SNAPSHOT_FAILED" 

1883 alert_str = ("Creating CBT snapshot for {} failed" 

1884 .format(snapVDI.uuid)) 

1885 snapVDI._disable_cbt_on_error(alert_name, alert_str) 

1886 pass 

1887 

1888 except (util.SMException, XenAPI.Failure) as e: 

1889 util.logException("LVMVDI._snapshot") 

1890 self._failClone(origUuid, jval, str(e)) 

1891 util.fistpoint.activate("LVHDRT_clone_vdi_before_remove_journal", self.sr.uuid) 

1892 

1893 self.sr.journaler.remove(self.JRN_CLONE, origUuid) 

1894 

1895 return self._finishSnapshot(snapVDI, snapVDI2, hostRefs, cloneOp, snapType) 

1896 

1897 def _createSnap(self, snapUuid, snapVdiType, snapSizeLV, isNew, is_mirror_destination=False): 

1898 """Snapshot self and return the snapshot VDI object""" 

1899 

1900 snapLV = LV_PREFIX[snapVdiType] + snapUuid 

1901 snapPath = os.path.join(self.sr.path, snapLV) 

1902 self.sr.lvmCache.create(snapLV, int(snapSizeLV)) 

1903 util.fistpoint.activate("LVHDRT_clone_vdi_after_lvcreate", self.sr.uuid) 

1904 if isNew: 

1905 RefCounter.set(snapUuid, 1, 0, NS_PREFIX_LVM + self.sr.uuid) 

1906 self.sr.lvActivator.add(snapUuid, snapLV, False) 

1907 parentRaw = (self.vdi_type == VdiType.RAW) 

1908 self.cowutil.snapshot( 

1909 snapPath, self.path, parentRaw, max(self.size, self.cowutil.getDefaultPreallocationSizeVirt()), is_mirror_image=is_mirror_destination 

1910 ) 

1911 snapParent = self.cowutil.getParent(snapPath, LvmCowUtil.extractUuid) 

1912 

1913 snapVDI = LVMVDI(self.sr, snapUuid) 

1914 snapVDI.read_only = False 

1915 snapVDI.location = snapUuid 

1916 snapVDI.size = self.size 

1917 snapVDI.utilisation = snapSizeLV 

1918 snapVDI.sm_config = dict() 

1919 for key, val in self.sm_config.items(): 1919 ↛ 1920line 1919 didn't jump to line 1920, because the loop on line 1919 never started

1920 if key not in [ 

1921 "type", "vdi_type", "vhd-parent", "paused", "relinking", "activating"] and \ 

1922 not key.startswith("host_"): 

1923 snapVDI.sm_config[key] = val 

1924 snapVDI.sm_config["vdi_type"] = snapVdiType 

1925 snapVDI.sm_config["vhd-parent"] = snapParent 

1926 # TODO: fix the raw snapshot case  

1927 snapVDI.sm_config["image-format"] = getImageStringFromVdiType(self.vdi_type) 

1928 snapVDI.lvname = snapLV 

1929 return snapVDI 

1930 

1931 def _finishSnapshot(self, snapVDI, snapVDI2, hostRefs, cloneOp=False, snapType=None): 

1932 if snapType is not VDI.SNAPSHOT_INTERNAL: 1932 ↛ 1934line 1932 didn't jump to line 1934, because the condition on line 1932 was never false

1933 self.sr._updateStats(self.sr.uuid, self.size) 

1934 basePresent = True 

1935 

1936 # Verify parent locator field of both children and delete basePath if 

1937 # unused 

1938 snapParent = snapVDI.sm_config["vhd-parent"] 

1939 snap2Parent = "" 

1940 if snapVDI2: 1940 ↛ 1942line 1940 didn't jump to line 1942, because the condition on line 1940 was never false

1941 snap2Parent = snapVDI2.sm_config["vhd-parent"] 

1942 if snapParent != self.uuid and \ 1942 ↛ 1976line 1942 didn't jump to line 1976, because the condition on line 1942 was never false

1943 (not snapVDI2 or snap2Parent != self.uuid): 

1944 util.SMlog("%s != %s != %s => deleting unused base %s" % \ 

1945 (snapParent, self.uuid, snap2Parent, self.lvname)) 

1946 RefCounter.put(self.uuid, False, NS_PREFIX_LVM + self.sr.uuid) 

1947 

1948 # The removed LV could still be activated on a slave host if it's 

1949 # part of a VM currently running there, we need to deactivate it 

1950 # before it gets removed to avoid a LV leak. 

1951 if hostRefs: 

1952 self.sr._deactivateOnSlave(hostRefs, self.lvname) 

1953 

1954 self.sr.lvmCache.remove(self.lvname) 

1955 self.sr.lvActivator.remove(self.uuid, False) 

1956 if hostRefs: 

1957 self.sr._updateSlavesOnRemove(hostRefs, self.uuid, self.lvname) 

1958 basePresent = False 

1959 else: 

1960 # assign the _binary_ refcount of the original VDI to the new base 

1961 # VDI (but as the normal refcount, since binary refcounts are only 

1962 # for leaf nodes). The normal refcount of the child is not 

1963 # transferred to to the base VDI because normal refcounts are 

1964 # incremented and decremented individually, and not based on the 

1965 # image chain (i.e., the child's normal refcount will be decremented 

1966 # independently of its parent situation). Add 1 for this clone op. 

1967 # Note that we do not need to do protect the refcount operations 

1968 # below with per-VDI locking like we do in lvutil because at this 

1969 # point we have exclusive access to the VDIs involved. Other SM 

1970 # operations are serialized by the Agent or with the SR lock, and 

1971 # any coalesce activations are serialized with the SR lock. (The 

1972 # coalesce activates the coalesced VDI pair in the beginning, which 

1973 # cannot affect the VDIs here because they cannot possibly be 

1974 # involved in coalescing at this point, and at the relinkSkip step 

1975 # that activates the children, which takes the SR lock.) 

1976 ns = NS_PREFIX_LVM + self.sr.uuid 

1977 (cnt, bcnt) = RefCounter.check(snapVDI.uuid, ns) 

1978 RefCounter.set(self.uuid, bcnt + 1, 0, ns) 

1979 

1980 # the "paused" and "host_*" sm-config keys are special and must stay on 

1981 # the leaf without being inherited by anyone else 

1982 for key in [x for x in self.sm_config.keys() if x == "paused" or x.startswith("host_")]: 1982 ↛ 1983line 1982 didn't jump to line 1983, because the loop on line 1982 never started

1983 snapVDI.sm_config[key] = self.sm_config[key] 

1984 del self.sm_config[key] 

1985 

1986 # Introduce any new VDI records & update the existing one 

1987 type = self.session.xenapi.VDI.get_type( \ 

1988 self.sr.srcmd.params['vdi_ref']) 

1989 if snapVDI2: 1989 ↛ 2031line 1989 didn't jump to line 2031, because the condition on line 1989 was never false

1990 LVMMetadataHandler(self.sr.mdpath).ensureSpaceIsAvailableForVdis(1) 

1991 vdiRef = snapVDI2._db_introduce() 

1992 if cloneOp: 

1993 vdi_info = {UUID_TAG: snapVDI2.uuid, 

1994 NAME_LABEL_TAG: util.to_plain_string( \ 

1995 self.session.xenapi.VDI.get_name_label( \ 

1996 self.sr.srcmd.params['vdi_ref'])), 

1997 NAME_DESCRIPTION_TAG: util.to_plain_string( \ 

1998 self.session.xenapi.VDI.get_name_description(self.sr.srcmd.params['vdi_ref'])), 

1999 IS_A_SNAPSHOT_TAG: 0, 

2000 SNAPSHOT_OF_TAG: '', 

2001 SNAPSHOT_TIME_TAG: '', 

2002 TYPE_TAG: type, 

2003 VDI_TYPE_TAG: snapVDI2.sm_config['vdi_type'], 

2004 READ_ONLY_TAG: 0, 

2005 MANAGED_TAG: int(snapVDI2.managed), 

2006 METADATA_OF_POOL_TAG: '' 

2007 } 

2008 else: 

2009 util.SMlog("snapshot VDI params: %s" % \ 

2010 self.session.xenapi.VDI.get_snapshot_time(vdiRef)) 

2011 vdi_info = {UUID_TAG: snapVDI2.uuid, 

2012 NAME_LABEL_TAG: util.to_plain_string( \ 

2013 self.session.xenapi.VDI.get_name_label( \ 

2014 self.sr.srcmd.params['vdi_ref'])), 

2015 NAME_DESCRIPTION_TAG: util.to_plain_string( \ 

2016 self.session.xenapi.VDI.get_name_description(self.sr.srcmd.params['vdi_ref'])), 

2017 IS_A_SNAPSHOT_TAG: 1, 

2018 SNAPSHOT_OF_TAG: snapVDI.uuid, 

2019 SNAPSHOT_TIME_TAG: '', 

2020 TYPE_TAG: type, 

2021 VDI_TYPE_TAG: snapVDI2.sm_config['vdi_type'], 

2022 READ_ONLY_TAG: 0, 

2023 MANAGED_TAG: int(snapVDI2.managed), 

2024 METADATA_OF_POOL_TAG: '' 

2025 } 

2026 

2027 LVMMetadataHandler(self.sr.mdpath).addVdi(vdi_info) 

2028 util.SMlog("vdi_clone: introduced 2nd snap VDI: %s (%s)" % \ 

2029 (vdiRef, snapVDI2.uuid)) 

2030 

2031 if basePresent: 2031 ↛ 2032line 2031 didn't jump to line 2032, because the condition on line 2031 was never true

2032 LVMMetadataHandler(self.sr.mdpath).ensureSpaceIsAvailableForVdis(1) 

2033 vdiRef = self._db_introduce() 

2034 vdi_info = {UUID_TAG: self.uuid, 

2035 NAME_LABEL_TAG: self.label, 

2036 NAME_DESCRIPTION_TAG: self.description, 

2037 IS_A_SNAPSHOT_TAG: 0, 

2038 SNAPSHOT_OF_TAG: '', 

2039 SNAPSHOT_TIME_TAG: '', 

2040 TYPE_TAG: type, 

2041 VDI_TYPE_TAG: self.sm_config['vdi_type'], 

2042 READ_ONLY_TAG: 1, 

2043 MANAGED_TAG: 0, 

2044 METADATA_OF_POOL_TAG: '' 

2045 } 

2046 

2047 LVMMetadataHandler(self.sr.mdpath).addVdi(vdi_info) 

2048 util.SMlog("vdi_clone: introduced base VDI: %s (%s)" % \ 

2049 (vdiRef, self.uuid)) 

2050 

2051 # Update the original record 

2052 vdi_ref = self.sr.srcmd.params['vdi_ref'] 

2053 self.session.xenapi.VDI.set_sm_config(vdi_ref, snapVDI.sm_config) 

2054 self.session.xenapi.VDI.set_physical_utilisation(vdi_ref, \ 

2055 str(snapVDI.utilisation)) 

2056 

2057 # Return the info on the new snap VDI 

2058 snap = snapVDI2 

2059 if not snap: 2059 ↛ 2060line 2059 didn't jump to line 2060, because the condition on line 2059 was never true

2060 snap = self 

2061 if not basePresent: 

2062 # a single-snapshot of an empty VDI will be a noop, resulting 

2063 # in no new VDIs, so return the existing one. The GC wouldn't 

2064 # normally try to single-snapshot an empty image of course, but 

2065 # if an external snapshot operation manages to sneak in right 

2066 # before a snapshot-coalesce phase, we would get here 

2067 snap = snapVDI 

2068 return snap.get_params() 

2069 

2070 def _setType(self, vdiType: str) -> None: 

2071 self.vdi_type = vdiType 

2072 self.cowutil = getCowUtil(self.vdi_type) 

2073 self.lvmcowutil = LvmCowUtil(self.cowutil) 

2074 

2075 def _initFromVDIInfo(self, vdiInfo): 

2076 self._setType(vdiInfo.vdiType) 

2077 self.lvname = vdiInfo.lvName 

2078 self.size = vdiInfo.sizeVirt 

2079 self.utilisation = vdiInfo.sizeLV 

2080 self.hidden = vdiInfo.hidden 

2081 if self.hidden: 2081 ↛ 2082line 2081 didn't jump to line 2082, because the condition on line 2081 was never true

2082 self.managed = False 

2083 self.active = vdiInfo.lvActive 

2084 self.readonly = vdiInfo.lvReadonly 

2085 self.parent = vdiInfo.parentUuid 

2086 self.path = os.path.join(self.sr.path, self.lvname) 

2087 if hasattr(self, "sm_config_override"): 2087 ↛ 2090line 2087 didn't jump to line 2090, because the condition on line 2087 was never false

2088 self.sm_config_override["vdi_type"] = self.vdi_type 

2089 else: 

2090 self.sm_config_override = {'vdi_type': self.vdi_type} 

2091 self.loaded = True 

2092 

2093 def _initFromLVInfo(self, lvInfo): 

2094 self._setType(lvInfo.vdiType) 

2095 self.lvname = lvInfo.name 

2096 self.size = lvInfo.size 

2097 self.utilisation = lvInfo.size 

2098 self.hidden = lvInfo.hidden 

2099 self.active = lvInfo.active 

2100 self.readonly = lvInfo.readonly 

2101 self.parent = '' 

2102 self.path = os.path.join(self.sr.path, self.lvname) 

2103 if hasattr(self, "sm_config_override"): 2103 ↛ 2106line 2103 didn't jump to line 2106, because the condition on line 2103 was never false

2104 self.sm_config_override["vdi_type"] = self.vdi_type 

2105 else: 

2106 self.sm_config_override = {'vdi_type': self.vdi_type} 

2107 if 'vhd-parent' in self.sm_config_override: 2107 ↛ 2108line 2107 didn't jump to line 2108, because the condition on line 2107 was never true

2108 self.parent = self.sm_config_override['vhd-parent'] 

2109 if not VdiType.isCowImage(self.vdi_type): 2109 ↛ 2110line 2109 didn't jump to line 2110, because the condition on line 2109 was never true

2110 self.loaded = True 

2111 

2112 def _initFromImageInfo(self, imageInfo): 

2113 self.size = imageInfo.sizeVirt 

2114 if self.parent == '' or (imageInfo.parentUuid != '' and imageInfo.parentUuid != self.parent): 2114 ↛ 2116line 2114 didn't jump to line 2116, because the condition on line 2114 was never false

2115 self.parent = imageInfo.parentUuid 

2116 self.hidden = imageInfo.hidden 

2117 self.loaded = True 

2118 

2119 def _determineType(self): 

2120 """ 

2121 Determine whether this is a RAW or a COW VDI. 

2122 """ 

2123 if "vdi_ref" in self.sr.srcmd.params: 

2124 vdi_ref = self.sr.srcmd.params["vdi_ref"] 

2125 sm_config = self.session.xenapi.VDI.get_sm_config(vdi_ref) 

2126 if sm_config.get("vdi_type"): 2126 ↛ 2127line 2126 didn't jump to line 2127, because the condition on line 2126 was never true

2127 self._setType(sm_config["vdi_type"]) 

2128 prefix = LV_PREFIX[self.vdi_type] 

2129 self.lvname = "%s%s" % (prefix, self.uuid) 

2130 self.path = os.path.join(self.sr.path, self.lvname) 

2131 self.sm_config_override = sm_config 

2132 return True 

2133 

2134 # LVM commands can be costly, so check the file directly first in case 

2135 # the LV is active 

2136 found = False 

2137 for vdi_type, prefix in LV_PREFIX.items(): 

2138 lvname = "%s%s" % (prefix, self.uuid) 

2139 path = os.path.join(self.sr.path, lvname) 

2140 if util.pathexists(path): 

2141 if found: 2141 ↛ 2142line 2141 didn't jump to line 2142, because the condition on line 2141 was never true

2142 raise xs_errors.XenError('VDILoad', 

2143 opterr="multiple VDI's: uuid %s" % self.uuid) 

2144 found = True 

2145 self._setType(vdi_type) 

2146 self.lvname = lvname 

2147 self.path = path 

2148 if found: 

2149 return True 

2150 

2151 # now list all LV's 

2152 if not lvutil._checkVG(self.sr.vgname): 2152 ↛ 2154line 2152 didn't jump to line 2154, because the condition on line 2152 was never true

2153 # when doing attach_from_config, the VG won't be there yet 

2154 return False 

2155 

2156 lvs = LvmCowUtil.getVolumeInfo(self.sr.lvmCache) 

2157 if lvs.get(self.uuid): 2157 ↛ 2160line 2157 didn't jump to line 2160, because the condition on line 2157 was never false

2158 self._initFromLVInfo(lvs[self.uuid]) 

2159 return True 

2160 return False 

2161 

2162 def _loadThis(self): 

2163 """ 

2164 Load VDI info for this VDI and activate the LV if it's COW. We 

2165 don't do it in VDI.load() because not all VDI operations need it. 

2166 """ 

2167 if self.loaded: 2167 ↛ 2168line 2167 didn't jump to line 2168, because the condition on line 2167 was never true

2168 if VdiType.isCowImage(self.vdi_type): 

2169 self.sr.lvActivator.activate(self.uuid, self.lvname, False) 

2170 return 

2171 try: 

2172 lvs = LvmCowUtil.getVolumeInfo(self.sr.lvmCache, self.lvname) 

2173 except util.CommandException as e: 

2174 raise xs_errors.XenError('VDIUnavailable', 

2175 opterr='%s (LV scan error)' % os.strerror(abs(e.code))) 

2176 if not lvs.get(self.uuid): 2176 ↛ 2177line 2176 didn't jump to line 2177, because the condition on line 2176 was never true

2177 raise xs_errors.XenError('VDIUnavailable', opterr='LV not found') 

2178 self._initFromLVInfo(lvs[self.uuid]) 

2179 if VdiType.isCowImage(self.vdi_type): 2179 ↛ 2185line 2179 didn't jump to line 2185, because the condition on line 2179 was never false

2180 self.sr.lvActivator.activate(self.uuid, self.lvname, False) 

2181 imageInfo = self.cowutil.getInfo(self.path, LvmCowUtil.extractUuid, False) 

2182 if not imageInfo: 2182 ↛ 2183line 2182 didn't jump to line 2183, because the condition on line 2182 was never true

2183 raise xs_errors.XenError('VDIUnavailable', opterr='getInfo failed') 

2184 self._initFromImageInfo(imageInfo) 

2185 self.loaded = True 

2186 

2187 def _chainSetActive(self, active, binary, persistent=False): 

2188 if binary: 2188 ↛ 2189line 2188 didn't jump to line 2189, because the condition on line 2188 was never true

2189 (count, bcount) = RefCounter.checkLocked(self.uuid, 

2190 NS_PREFIX_LVM + self.sr.uuid) 

2191 if (active and bcount > 0) or (not active and bcount == 0): 

2192 return # this is a redundant activation/deactivation call 

2193 

2194 vdiList = {self.uuid: self.lvname} 

2195 if VdiType.isCowImage(self.vdi_type): 2195 ↛ 2197line 2195 didn't jump to line 2197, because the condition on line 2195 was never false

2196 vdiList = self.cowutil.getParentChain(self.lvname, LvmCowUtil.extractUuid, self.sr.vgname) 

2197 for uuid, lvName in vdiList.items(): 2197 ↛ 2198line 2197 didn't jump to line 2198, because the loop on line 2197 never started

2198 binaryParam = binary 

2199 if uuid != self.uuid: 

2200 binaryParam = False # binary param only applies to leaf nodes 

2201 if active: 

2202 self.sr.lvActivator.activate(uuid, lvName, binaryParam, 

2203 persistent) 

2204 else: 

2205 # just add the LVs for deactivation in the final (cleanup) 

2206 # step. The LVs must not have been activated during the current 

2207 # operation 

2208 self.sr.lvActivator.add(uuid, lvName, binaryParam) 

2209 

2210 def _failClone(self, uuid, jval, msg): 

2211 try: 

2212 self.sr._handleInterruptedCloneOp(uuid, jval, True) 

2213 self.sr.journaler.remove(self.JRN_CLONE, uuid) 

2214 except Exception as e: 

2215 util.SMlog('WARNING: failed to clean up failed snapshot: ' \ 

2216 ' %s (error ignored)' % e) 

2217 raise xs_errors.XenError('VDIClone', opterr=msg) 

2218 

2219 def _markHidden(self): 

2220 if not VdiType.isCowImage(self.vdi_type): 

2221 self.sr.lvmCache.setHidden(self.lvname) 

2222 else: 

2223 self.cowutil.setHidden(self.path) 

2224 self.hidden = 1 

2225 

2226 def _prepareThin(self, attach, vdiType): 

2227 origUtilisation = self.sr.lvmCache.getSize(self.lvname) 

2228 if self.sr.isMaster: 

2229 # the master can prepare the VDI locally 

2230 if attach: 

2231 self.lvmcowutil.attachThin(self.sr.journaler, self.sr.uuid, self.uuid, self.vdi_type) 

2232 else: 

2233 self.lvmcowutil.detachThin(self.session, self.sr.lvmCache, self.sr.uuid, self.uuid, self.vdi_type) 

2234 else: 

2235 fn = "attach" 

2236 if not attach: 

2237 fn = "detach" 

2238 pools = self.session.xenapi.pool.get_all() 

2239 master = self.session.xenapi.pool.get_master(pools[0]) 

2240 rv = self.session.xenapi.host.call_plugin( 

2241 master, 

2242 self.sr.THIN_PLUGIN, 

2243 fn, 

2244 { 

2245 "srUuid": self.sr.uuid, 

2246 "vdiUuid": self.uuid, 

2247 "vdiType": vdiType 

2248 } 

2249 ) 

2250 util.SMlog("call-plugin returned: %s" % rv) 

2251 if not rv: 

2252 raise Exception('plugin %s failed' % self.sr.THIN_PLUGIN) 

2253 # refresh to pick up the size change on this slave 

2254 self.sr.lvmCache.activateNoRefcount(self.lvname, True) 

2255 

2256 self.utilisation = self.sr.lvmCache.getSize(self.lvname) 

2257 if origUtilisation != self.utilisation: 

2258 vdi_ref = self.sr.srcmd.params['vdi_ref'] 

2259 self.session.xenapi.VDI.set_physical_utilisation(vdi_ref, 

2260 str(self.utilisation)) 

2261 stats = lvutil._getVGstats(self.sr.vgname) 

2262 sr_utilisation = stats['physical_utilisation'] 

2263 self.session.xenapi.SR.set_physical_utilisation(self.sr.sr_ref, 

2264 str(sr_utilisation)) 

2265 

2266 def _make_chain_rw(self): 

2267 """ 

2268 In the case of QCOW2, we need the chain to be RW for the coalesce to be done by tapdisk. 

2269 Setting the chain to be RW early avoids having to stop the tapdisk so LVM can acknowledge the chain RW on the slave. 

2270 And while making the change to RW from the slave works without the refresh, it can cause LVM metadata corruption. 

2271 """ 

2272 # TODO(XCPNG-3689): We could check if any VDI on the chain is RO before doing the call to master to change this 

2273 if self.sr.isMaster: 

2274 lvmcache = self.sr.lvmCache 

2275 for uuid, lvName in self.cowutil.getParentChain(self.lvname, LvmCowUtil.extractUuid, self.sr.vgname).items(): 

2276 lvmcache.setReadonly(lvName, False) 

2277 else: 

2278 master = util.get_master_ref(self.session) 

2279 response = self.session.xenapi.host.call_plugin( 

2280 master, 

2281 self.sr.PLUGIN_ON_SLAVE, 

2282 "make_chain_rw", 

2283 { 

2284 "vgName": self.sr.vgname, 

2285 "lvName": self.lvname, 

2286 "vdiType": self.vdi_type 

2287 } 

2288 ) 

2289 util.SMlog(f"call-plugin returned: {response}") 

2290 if not response: 

2291 raise Exception(f"plugin {self.sr.PLUGIN_ON_SLAVE} failed") 

2292 

2293 @override 

2294 def update(self, sr_uuid, vdi_uuid) -> None: 

2295 if self.sr.legacyMode: 

2296 return 

2297 

2298 #Synch the name_label of this VDI on storage with the name_label in XAPI 

2299 vdi_ref = self.session.xenapi.VDI.get_by_uuid(self.uuid) 

2300 update_map = {} 

2301 update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] = \ 

2302 METADATA_OBJECT_TYPE_VDI 

2303 update_map[UUID_TAG] = self.uuid 

2304 update_map[NAME_LABEL_TAG] = util.to_plain_string( \ 

2305 self.session.xenapi.VDI.get_name_label(vdi_ref)) 

2306 update_map[NAME_DESCRIPTION_TAG] = util.to_plain_string( \ 

2307 self.session.xenapi.VDI.get_name_description(vdi_ref)) 

2308 update_map[SNAPSHOT_TIME_TAG] = \ 

2309 self.session.xenapi.VDI.get_snapshot_time(vdi_ref) 

2310 update_map[METADATA_OF_POOL_TAG] = \ 

2311 self.session.xenapi.VDI.get_metadata_of_pool(vdi_ref) 

2312 LVMMetadataHandler(self.sr.mdpath).updateMetadata(update_map) 

2313 

2314 @override 

2315 def _ensure_cbt_space(self) -> None: 

2316 # We need virtual_size to compute the size in case of a bigger VDI 

2317 self.sr.ensureCBTSpace(self.size) 

2318 

2319 @override 

2320 def _create_cbt_log(self) -> str: 

2321 logname = self._get_cbt_logname(self.uuid) 

2322 logsize = max(util.roundup(CBT_BLOCK_SIZE, self.size//CBT_BLOCK_SIZE), self.sr.journaler.LV_SIZE) 

2323 # We choose 4MiB as the minimum for the log size to maintain the old behavior and compute the correct amount 

2324 # if we need a bigger LV for the CBT (can happen with big QCOW2) 

2325 self.sr.lvmCache.create(logname, logsize, CBTLOG_TAG) 

2326 logpath = super(LVMVDI, self)._create_cbt_log() 

2327 self.sr.lvmCache.deactivateNoRefcount(logname) 

2328 return logpath 

2329 

2330 @override 

2331 def _delete_cbt_log(self) -> None: 

2332 logpath = self._get_cbt_logpath(self.uuid) 

2333 if self._cbt_log_exists(logpath): 

2334 logname = self._get_cbt_logname(self.uuid) 

2335 self.sr.lvmCache.remove(logname) 

2336 

2337 @override 

2338 def _rename(self, oldpath, newpath) -> None: 

2339 oldname = os.path.basename(oldpath) 

2340 newname = os.path.basename(newpath) 

2341 self.sr.lvmCache.rename(oldname, newname) 

2342 

2343 @override 

2344 def update_slaves_on_cbt_disable(self, cbtlog) -> None: 

2345 args = { 

2346 "vgName": self.sr.vgname, 

2347 "action1": "deactivateNoRefcount", 

2348 "lvName1": cbtlog 

2349 } 

2350 

2351 host_refs = util.get_hosts_attached_on(self.session, [self.uuid]) 

2352 

2353 message = f"Deactivating {cbtlog}" 

2354 self.sr.call_on_slave(args, host_refs, message) 

2355 

2356 @override 

2357 def _activate_cbt_log(self, lv_name) -> bool: 

2358 self.sr.lvmCache.refresh() 

2359 if not self.sr.lvmCache.is_active(lv_name): 2359 ↛ 2360line 2359 didn't jump to line 2360, because the condition on line 2359 was never true

2360 try: 

2361 self.sr.lvmCache.activateNoRefcount(lv_name) 

2362 return True 

2363 except Exception as e: 

2364 util.SMlog("Exception in _activate_cbt_log, " 

2365 "Error: %s." % str(e)) 

2366 raise 

2367 else: 

2368 return False 

2369 

2370 @override 

2371 def _deactivate_cbt_log(self, lv_name) -> None: 

2372 try: 

2373 self.sr.lvmCache.deactivateNoRefcount(lv_name) 

2374 except Exception as e: 

2375 util.SMlog("Exception in _deactivate_cbt_log, Error: %s." % str(e)) 

2376 raise 

2377 

2378 @override 

2379 def _cbt_log_exists(self, logpath) -> bool: 

2380 return lvutil.exists(logpath) 

2381 

2382if __name__ == '__main__': 2382 ↛ 2383line 2382 didn't jump to line 2383, because the condition on line 2382 was never true

2383 SRCommand.run(LVMSR, DRIVER_INFO) 

2384else: 

2385 SR.registerSR(LVMSR)