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 PLUGIN_ON_MASTER = "on-master" 

99 

100 FLAG_USE_VHD = "use_vhd" 

101 MDVOLUME_NAME = "MGT" 

102 

103 ALLOCATION_QUANTUM = "allocation_quantum" 

104 INITIAL_ALLOCATION = "initial_allocation" 

105 

106 LOCK_RETRY_INTERVAL = 3 

107 LOCK_RETRY_ATTEMPTS = 10 

108 

109 TEST_MODE_KEY = "testmode" 

110 TEST_MODE_VHD_FAIL_REPARENT_BEGIN = "vhd_fail_reparent_begin" 

111 TEST_MODE_VHD_FAIL_REPARENT_LOCATOR = "vhd_fail_reparent_locator" 

112 TEST_MODE_VHD_FAIL_REPARENT_END = "vhd_fail_reparent_end" 

113 TEST_MODE_VHD_FAIL_RESIZE_BEGIN = "vhd_fail_resize_begin" 

114 TEST_MODE_VHD_FAIL_RESIZE_DATA = "vhd_fail_resize_data" 

115 TEST_MODE_VHD_FAIL_RESIZE_METADATA = "vhd_fail_resize_metadata" 

116 TEST_MODE_VHD_FAIL_RESIZE_END = "vhd_fail_resize_end" 

117 

118 ENV_VAR_VHD_TEST = { 

119 TEST_MODE_VHD_FAIL_REPARENT_BEGIN: 

120 "VHD_UTIL_TEST_FAIL_REPARENT_BEGIN", 

121 TEST_MODE_VHD_FAIL_REPARENT_LOCATOR: 

122 "VHD_UTIL_TEST_FAIL_REPARENT_LOCATOR", 

123 TEST_MODE_VHD_FAIL_REPARENT_END: 

124 "VHD_UTIL_TEST_FAIL_REPARENT_END", 

125 TEST_MODE_VHD_FAIL_RESIZE_BEGIN: 

126 "VHD_UTIL_TEST_FAIL_RESIZE_BEGIN", 

127 TEST_MODE_VHD_FAIL_RESIZE_DATA: 

128 "VHD_UTIL_TEST_FAIL_RESIZE_DATA_MOVED", 

129 TEST_MODE_VHD_FAIL_RESIZE_METADATA: 

130 "VHD_UTIL_TEST_FAIL_RESIZE_METADATA_MOVED", 

131 TEST_MODE_VHD_FAIL_RESIZE_END: 

132 "VHD_UTIL_TEST_FAIL_RESIZE_END" 

133 } 

134 testMode = "" 

135 

136 legacyMode = True 

137 

138 @override 

139 @staticmethod 

140 def handles(type) -> bool: 

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

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

143 if __name__ == '__main__': 

144 name = sys.argv[0] 

145 else: 

146 name = __name__ 

147 if name.endswith("LVMSR"): 

148 return type == "lvm" 

149 elif name.endswith("EXTSR"): 

150 return type == "ext" 

151 return type == LVMSR.DRIVER_TYPE 

152 

153 def __init__(self, srcmd, sr_uuid): 

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

155 self._init_image_formats() 

156 

157 @override 

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

159 self.ops_exclusive = OPS_EXCLUSIVE 

160 

161 self.isMaster = False 

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

163 self.isMaster = True 

164 

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

166 self.sr_vditype = SR.DEFAULT_TAP 

167 self.uuid = sr_uuid 

168 self.vgname = VG_PREFIX + self.uuid 

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

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

171 self.provision = self.PROVISIONING_DEFAULT 

172 

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

174 if has_sr_ref: 

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

176 else: 

177 self.other_conf = None 

178 

179 self.lvm_conf = None 

180 if self.other_conf: 

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

182 

183 try: 

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

185 except: 

186 raise xs_errors.XenError('SRUnavailable', \ 

187 opterr='Failed to initialise the LVMCache') 

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

189 self.journaler = Journaler(self.lvmCache) 

190 if not has_sr_ref: 

191 return # must be a probe call 

192 # Test for thick vs thin provisioning conf parameter 

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

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

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

196 else: 

197 raise xs_errors.XenError('InvalidArg', \ 

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

199 

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

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

202 self._prepareTestMode() 

203 

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

205 # sm_config flag overrides PBD, if any 

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

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

208 

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

210 self.legacyMode = False 

211 

212 if lvutil._checkVG(self.vgname): 

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

214 "vdi_activate", "vdi_deactivate"]: 

215 self._undoAllJournals() 

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

217 self._checkMetadataVolume() 

218 

219 self.mdexists = False 

220 

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

222 contains_uuid_regex = \ 

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

224 self.storageVDIs = {} 

225 

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

227 # if the lvname has a uuid in it 

228 type = None 

229 vdi = None 

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

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

232 if key.startswith(prefix): 

233 vdi = key[len(prefix):] 

234 self.storageVDIs[vdi] = vdi_type 

235 break 

236 

237 # check if metadata volume exists 

238 try: 

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

240 except: 

241 pass 

242 

243 @override 

244 def cleanup(self) -> None: 

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

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

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

248 

249 def updateSRMetadata(self, allocation): 

250 try: 

251 # Add SR specific SR metadata 

252 sr_info = \ 

253 {ALLOCATION_TAG: allocation, 

254 UUID_TAG: self.uuid, 

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

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

257 } 

258 

259 vdi_info = {} 

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

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

262 is_a_snapshot = int(self.session.xenapi.VDI.get_is_a_snapshot(vdi)) 

263 

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

265 if not vdi_type: 

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

267 

268 # Create the VDI entry in the SR metadata 

269 vdi_info[vdi_uuid] = \ 

270 { 

271 UUID_TAG: vdi_uuid, 

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

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

274 IS_A_SNAPSHOT_TAG: \ 

275 is_a_snapshot, 

276 SNAPSHOT_OF_TAG: \ 

277 self.session.xenapi.VDI.get_uuid(self.session.xenapi.VDI.get_snapshot_of(vdi)) if bool(is_a_snapshot) else '', 

278 SNAPSHOT_TIME_TAG: \ 

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

280 TYPE_TAG: \ 

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

282 VDI_TYPE_TAG: \ 

283 vdi_type, 

284 READ_ONLY_TAG: \ 

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

286 METADATA_OF_POOL_TAG: \ 

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

288 MANAGED_TAG: \ 

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

290 } 

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

292 

293 except Exception as e: 

294 raise xs_errors.XenError('MetadataError', \ 

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

296 

297 def syncMetadataAndStorage(self): 

298 try: 

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

300 # then delete it from the metadata 

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

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

303 update_map = {} 

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

305 # delete this from metadata 

306 LVMMetadataHandler(self.mdpath). \ 

307 deleteVdiFromMetadata(vdi_info[vdi][UUID_TAG]) 

308 else: 

309 # search for this in the metadata, compare types 

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

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

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

313 # storage type takes authority 

314 update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] \ 

315 = METADATA_OBJECT_TYPE_VDI 

316 update_map[UUID_TAG] = vdi_info[vdi][UUID_TAG] 

317 update_map[VDI_TYPE_TAG] = \ 

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

319 LVMMetadataHandler(self.mdpath) \ 

320 .updateMetadata(update_map) 

321 else: 

322 # This should never happen 

323 pass 

324 

325 except Exception as e: 

326 raise xs_errors.XenError('MetadataError', \ 

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

328 

329 def syncMetadataAndXapi(self): 

330 try: 

331 # get metadata 

332 (sr_info, vdi_info) = \ 

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

334 

335 # First synch SR parameters 

336 self.update(self.uuid) 

337 

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

339 for vdi_offset in vdi_info.keys(): 

340 try: 

341 vdi_ref = \ 

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

343 vdi_info[vdi_offset][UUID_TAG]) 

344 except: 

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

346 continue 

347 

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

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

350 

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

352 vdi_info[vdi_offset][NAME_DESCRIPTION_TAG] != \ 

353 new_name_description: 

354 update_map = {} 

355 update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] = \ 

356 METADATA_OBJECT_TYPE_VDI 

357 update_map[UUID_TAG] = vdi_info[vdi_offset][UUID_TAG] 

358 update_map[NAME_LABEL_TAG] = new_name_label 

359 update_map[NAME_DESCRIPTION_TAG] = new_name_description 

360 LVMMetadataHandler(self.mdpath) \ 

361 .updateMetadata(update_map) 

362 except Exception as e: 

363 raise xs_errors.XenError('MetadataError', \ 

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

365 

366 def _checkMetadataVolume(self): 

367 util.SMlog("Entering _checkMetadataVolume") 

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

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

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

371 try: 

372 # activate the management volume 

373 # will be deactivated at detach time 

374 self.lvmCache.activateNoRefcount(self.MDVOLUME_NAME) 

375 self._synchSmConfigWithMetaData() 

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

377 self.syncMetadataAndStorage() 

378 self.syncMetadataAndXapi() 

379 except Exception as e: 

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

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

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

383 self._introduceMetaDataVolume() 

384 

385 if self.mdexists: 

386 self.legacyMode = False 

387 

388 def _synchSmConfigWithMetaData(self): 

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

390 

391 try: 

392 # get SR info from metadata 

393 sr_info = {} 

394 map = {} 

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

396 

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

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

399 

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

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

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

403 else: 

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

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

406 

407 except Exception as e: 

408 raise xs_errors.XenError( 

409 'MetadataError', 

410 opterr='Error reading SR params from ' 

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

412 try: 

413 map[self.FLAG_USE_VHD] = 'true' 

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

415 except: 

416 raise xs_errors.XenError( 

417 'MetadataError', 

418 opterr='Error updating sm_config key') 

419 

420 def _introduceMetaDataVolume(self): 

421 util.SMlog("Creating Metadata volume") 

422 try: 

423 config = {} 

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

425 

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

427 self.lvmCache.activateNoRefcount(self.MDVOLUME_NAME) 

428 

429 name_label = util.to_plain_string( \ 

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

431 name_description = util.to_plain_string( \ 

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

433 config[self.FLAG_USE_VHD] = "true" 

434 config['allocation'] = self.provision 

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

436 

437 # Add the SR metadata 

438 self.updateSRMetadata(self.provision) 

439 except Exception as e: 

440 raise xs_errors.XenError('MetadataError', \ 

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

442 

443 def _removeMetadataVolume(self): 

444 if self.mdexists: 

445 try: 

446 self.lvmCache.remove(self.MDVOLUME_NAME) 

447 except: 

448 raise xs_errors.XenError('MetadataError', \ 

449 opterr='Failed to delete MGT Volume') 

450 

451 def _refresh_size(self): 

452 """ 

453 Refreshs the size of the backing device. 

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

455 """ 

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

457 # LVMoHBASR, LVMoISCSISR 

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

459 else: 

460 # LVMSR 

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

462 scsiutil.refreshdev(devices) 

463 return True 

464 

465 def _expand_size(self): 

466 """ 

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

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

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

470 """ 

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

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

473 resizethreshold = 100 * 1024 * 1024 # 100MB 

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

475 totaldevicesize = 0 

476 for device in devices: 

477 totaldevicesize = totaldevicesize + scsiutil.getsize(device) 

478 if totaldevicesize >= (currentvgsize + resizethreshold): 

479 try: 

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

481 # LVMoHBASR, LVMoISCSISR might have slaves 

482 scsiutil.refresh_lun_size_by_SCSIid_on_slaves(self.session, 

483 getattr(self, 'SCSIid')) 

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

485 self.uuid) 

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

487 lvutil.resizePV(pv) 

488 except: 

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

490 " the PV" % self.uuid) 

491 

492 @override 

493 @deviceCheck 

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

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

496 if not self.isMaster: 

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

498 raise xs_errors.XenError('LVMMaster') 

499 

500 if lvutil._checkVG(self.vgname): 

501 raise xs_errors.XenError('SRExists') 

502 

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

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

505 raise xs_errors.XenError('SRInUse') 

506 

507 # Check serial number entry in SR records 

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

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

510 raise xs_errors.XenError('SRInUse') 

511 

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

513 

514 #Update serial number string 

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

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

517 

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

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

520 self.FLAG_USE_VHD, 'true') 

521 

522 @override 

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

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

525 if not self.isMaster: 

526 raise xs_errors.XenError('LVMMaster') 

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

528 

529 success = True 

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

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

532 continue 

533 

534 if util.doesFileHaveOpenHandles(fileName): 

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

536 "handles" % fileName) 

537 success = False 

538 continue 

539 

540 # Now attempt to remove the dev mapper entry 

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

542 success = False 

543 continue 

544 

545 try: 

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

547 replace('//', '-')) 

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

549 os.unlink(lpath) 

550 except OSError as e: 

551 if e.errno != errno.ENOENT: 

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

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

554 success = False 

555 

556 if success: 

557 try: 

558 if util.pathexists(self.path): 

559 os.rmdir(self.path) 

560 except Exception as e: 

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

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

563 success = False 

564 

565 self._removeMetadataVolume() 

566 self.lvmCache.refresh() 

567 if LvmCowUtil.getVolumeInfo(self.lvmCache): 

568 raise xs_errors.XenError('SRNotEmpty') 

569 

570 if not success: 

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

572 "for details.") 

573 

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

575 self._cleanup() 

576 

577 @override 

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

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

580 

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

582 

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

584 raise xs_errors.XenError('SRUnavailable', \ 

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

586 

587 # Refresh the metadata status 

588 self._checkMetadataVolume() 

589 

590 refreshsizeok = self._refresh_size() 

591 

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

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

594 self._expand_size() 

595 

596 # Update SCSIid string 

597 util.SMlog("Calling devlist_to_serial") 

598 scsiutil.add_serial_record( 

599 self.session, self.sr_ref, 

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

601 

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

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

604 vdiInfo = LvmCowUtil.getVDIInfo(self.lvmCache) 

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

606 if VdiType.isCowImage(info.vdiType): 

607 self.legacyMode = False 

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

609 self._introduceMetaDataVolume() 

610 break 

611 

612 # Set the block scheduler 

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

614 self.block_setscheduler(dev) 

615 

616 @override 

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

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

619 cleanup.abort(self.uuid) 

620 

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

622 # go through all devmapper entries for this VG 

623 success = True 

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

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

626 continue 

627 

628 with Fairlock('devicemapper'): 

629 # check if any file has open handles 

630 if util.doesFileHaveOpenHandles(fileName): 

631 # if yes, log this and signal failure 

632 util.SMlog( 

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

634 "open handles") 

635 success = False 

636 continue 

637 

638 # Now attempt to remove the dev mapper entry 

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

640 success = False 

641 continue 

642 

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

644 try: 

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

646 replace('//', '-')) 

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

648 util.force_unlink(lvname) 

649 except Exception as e: 

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

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

652 success = False 

653 

654 # now remove the directory where the symlinks are 

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

656 if success: 

657 try: 

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

659 os.rmdir(self.path) 

660 except Exception as e: 

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

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

663 success = False 

664 

665 if not success: 

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

667 "for details.") 

668 

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

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

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

672 # only place to do so. 

673 self._cleanup(self.isMaster) 

674 

675 @override 

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

677 if not self.legacyMode: 

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

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

680 

681 @override 

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

683 activated_lvs = set() 

684 try: 

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

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

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

688 raise xs_errors.XenError('LVMMaster') 

689 

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

691 self._expand_size() 

692 self.lvmCache.refresh() 

693 cbt_vdis = self.lvmCache.getTagged(CBTLOG_TAG) 

694 self._loadvdis() 

695 stats = lvutil._getVGstats(self.vgname) 

696 self.physical_size = stats['physical_size'] 

697 self.physical_utilisation = stats['physical_utilisation'] 

698 

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

700 # XAPI 

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

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

703 # get VDIs from XAPI 

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

705 vdi_uuids = set([]) 

706 for vdi in vdis: 

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

708 

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

710 

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

712 vdi_uuid = info[vdi][UUID_TAG] 

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

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

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

716 else: 

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

718 

719 if vdi_uuid not in vdi_uuids: 

720 vdi_type = info[vdi][VDI_TYPE_TAG] 

721 sm_config = {} 

722 sm_config['vdi_type'] = vdi_type 

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

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

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

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

727 "removing from metadata." % 

728 (vdi_uuid, lvname)) 

729 LVMMetadataHandler(self.mdpath). \ 

730 deleteVdiFromMetadata(vdi_uuid) 

731 continue 

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

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

734 self.lvActivator.activate( 

735 vdi_uuid, lvname, LVActivator.NORMAL) 

736 activated_lvs.add(vdi_uuid) 

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

738 

739 if not VdiType.isCowImage(vdi_type): 

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

741 utilisation = \ 

742 util.roundup(lvutil.LVM_SIZE_INCREMENT, 

743 int(size)) 

744 else: 

745 cowutil = getCowUtil(vdi_type) 

746 lvmcowutil = LvmCowUtil(cowutil) 

747 

748 parent = cowutil.getParentNoCheck(lvPath) 

749 

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

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

752 size = cowutil.getSizeVirt(lvPath) 

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

754 utilisation = util.roundup( 

755 lvutil.LVM_SIZE_INCREMENT, 

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

757 ) 

758 else: 

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

760 

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

762 vdi_uuid, 

763 info[vdi][NAME_LABEL_TAG], 

764 info[vdi][NAME_DESCRIPTION_TAG], 

765 self.sr_ref, 

766 info[vdi][TYPE_TAG], 

767 False, 

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

769 {}, 

770 vdi_uuid, 

771 {}, 

772 sm_config) 

773 

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

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

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

777 str(size)) 

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

779 vdi_ref, str(utilisation)) 

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

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

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

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

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

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

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

787 vdi_ref, info[vdi][METADATA_OF_POOL_TAG]) 

788 

789 # Update CBT status of disks either just added 

790 # or already in XAPI 

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

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

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

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

795 # For existing VDIs, update local state too 

796 # Scan in base class SR updates existing VDIs 

797 # again based on local states 

798 if vdi_uuid in self.vdis: 

799 self.vdis[vdi_uuid].cbt_enabled = True 

800 cbt_vdis.remove(cbt_logname) 

801 

802 # Now set the snapshot statuses correctly in XAPI 

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

804 try: 

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

806 except: 

807 # the source VDI no longer exists, continue 

808 continue 

809 

810 for snapvdi in vdiToSnaps[srcvdi]: 

811 try: 

812 # this might fail in cases where its already set 

813 snapref = \ 

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

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

816 except Exception as e: 

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

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

819 

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

821 # If we have items remaining in this list, 

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

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

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

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

826 new_vdi = self.vdi(cbt_uuid) 

827 new_vdi.ty = "cbt_metadata" 

828 new_vdi.cbt_enabled = True 

829 self.vdis[cbt_uuid] = new_vdi 

830 

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

832 self._kickGC() 

833 

834 finally: 

835 for vdi in activated_lvs: 

836 self.lvActivator.deactivate( 

837 vdi, LVActivator.NORMAL, False) 

838 

839 @override 

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

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

842 return 

843 self._updateStats(uuid, 0) 

844 

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

846 return 

847 

848 # synch name_label in metadata with XAPI 

849 update_map = {} 

850 update_map = {METADATA_UPDATE_OBJECT_TYPE_TAG: \ 

851 METADATA_OBJECT_TYPE_SR, 

852 NAME_LABEL_TAG: util.to_plain_string( \ 

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

854 NAME_DESCRIPTION_TAG: util.to_plain_string( \ 

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

856 } 

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

858 

859 def _updateStats(self, uuid, virtAllocDelta): 

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

861 self.virtual_allocation = valloc + virtAllocDelta 

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

863 (uuid, self.virtual_allocation)) 

864 stats = lvutil._getVGstats(self.vgname) 

865 self.physical_size = stats['physical_size'] 

866 self.physical_utilisation = stats['physical_utilisation'] 

867 self._db_update() 

868 

869 @override 

870 @deviceCheck 

871 def probe(self) -> str: 

872 return lvutil.srlist_toxml( 

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

874 VG_PREFIX, 

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

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

877 

878 @override 

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

880 return LVMVDI(self, uuid) 

881 

882 def _loadvdis(self): 

883 self.virtual_allocation = 0 

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

885 self.allVDIs = {} 

886 

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

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

889 continue 

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

891 raise xs_errors.XenError('VDIUnavailable', \ 

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

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

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

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

896 

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

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

899 if vdi.parent in self.vdis: 

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

901 if vdi.parent in geneology: 

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

903 else: 

904 geneology[vdi.parent] = [uuid] 

905 

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

907 # will be GC'ed 

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

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

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

911 del self.vdis[uuid] 

912 

913 def _ensureSpaceAvailable(self, amount_needed): 

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

915 if (space_available < amount_needed): 915 ↛ 916line 915 didn't jump to line 916, because the condition on line 915 was never true

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

917 (space_available, amount_needed)) 

918 raise xs_errors.XenError('SRNoSpace') 

919 

920 def _handleInterruptedCloneOps(self): 

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

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

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

924 self._handleInterruptedCloneOp(uuid, val) 

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

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

927 

928 def _handleInterruptedCoalesceLeaf(self): 

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

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

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

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

933 self.lvmCache.refresh() 

934 

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

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

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

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

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

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

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

942 lvs = LvmCowUtil.getVolumeInfo(self.lvmCache) 

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

944 

945 # is there a "base copy" VDI? 

946 if not lvs.get(baseUuid): 

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

948 if lvs.get(origUuid): 

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

950 return 

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

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

953 

954 vdis = LvmCowUtil.getVDIInfo(self.lvmCache) 

955 base = vdis[baseUuid] 

956 cowutil = getCowUtil(base.vdiType) 

957 

958 if forceUndo: 

959 util.SMlog("Explicit revert") 

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

961 return 

962 

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

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

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

966 return 

967 

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

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

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

971 return 

972 

973 orig = vdis[origUuid] 

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

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

976 if orig.parentUuid != baseUuid: 

977 parent = vdis[orig.parentUuid] 

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

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

980 

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

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

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

984 return 

985 

986 if clonUuid: 

987 clon = vdis[clonUuid] 

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

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

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

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

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

993 return 

994 

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

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

997 

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

999 base = lvs[baseUuid] 

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

1001 

1002 # make the parent RW 

1003 if base.readonly: 

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

1005 

1006 ns = NS_PREFIX_LVM + self.uuid 

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

1008 origRefcountNormal = 0 

1009 

1010 # un-hide the parent 

1011 if VdiType.isCowImage(base.vdiType): 

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

1013 origRefcountNormal = 1 

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

1015 if imageInfo.hidden: 

1016 cowutil.setHidden(basePath, False) 

1017 elif base.hidden: 

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

1019 

1020 # remove the child nodes 

1021 if clonUuid and lvs.get(clonUuid): 

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

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

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

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

1026 self.lvActivator.remove(clonUuid, False) 

1027 if lvs.get(origUuid): 

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

1029 

1030 # inflate the parent to fully-allocated size 

1031 if VdiType.isCowImage(base.vdiType): 

1032 lvmcowutil = LvmCowUtil(cowutil) 

1033 fullSize = lvmcowutil.calcVolumeSize(imageInfo.sizeVirt) 

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

1035 

1036 # rename back 

1037 origLV = LV_PREFIX[base.vdiType] + origUuid 

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

1039 RefCounter.reset(baseUuid, ns) 

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

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

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

1043 

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

1045 # flag to facilitate vm deactivate 

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

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

1048 

1049 # update LVM metadata on slaves 

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

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

1052 origLV, origUuid, slaves) 

1053 

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

1055 

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

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

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

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

1060 would cause a deadlock)""" 

1061 base = vdis[baseUuid] 

1062 clon = None 

1063 if clonUuid: 

1064 clon = vdis[clonUuid] 

1065 

1066 cleanup.abort(self.uuid) 

1067 

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

1069 if not base.hidden: 

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

1071 self.lvmCache.setHidden(base.lvName) 

1072 else: 

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

1074 cowutil.setHidden(basePath) 

1075 if not base.lvReadonly: 

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

1077 

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

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

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

1081 

1082 # Update the original record 

1083 try: 

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

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

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

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

1088 sm_config['vhd-parent'] = baseUuid 

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

1090 except XenAPI.Failure: 

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

1092 

1093 # introduce the new VDI records 

1094 if clonUuid: 

1095 try: 

1096 clon_vdi = VDI.VDI(self, clonUuid) 

1097 clon_vdi.read_only = False 

1098 clon_vdi.location = clonUuid 

1099 clon_vdi.utilisation = clon.sizeLV 

1100 clon_vdi.sm_config = { 

1101 "vdi_type": clon.vdiType, 

1102 "vhd-parent": baseUuid} 

1103 

1104 if not self.legacyMode: 

1105 LVMMetadataHandler(self.mdpath). \ 

1106 ensureSpaceIsAvailableForVdis(1) 

1107 

1108 clon_vdi_ref = clon_vdi._db_introduce() 

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

1110 (clon_vdi_ref, clonUuid)) 

1111 

1112 vdi_info = {UUID_TAG: clonUuid, 

1113 NAME_LABEL_TAG: clon_vdi.label, 

1114 NAME_DESCRIPTION_TAG: clon_vdi.description, 

1115 IS_A_SNAPSHOT_TAG: 0, 

1116 SNAPSHOT_OF_TAG: '', 

1117 SNAPSHOT_TIME_TAG: '', 

1118 TYPE_TAG: type, 

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

1120 READ_ONLY_TAG: int(clon_vdi.read_only), 

1121 MANAGED_TAG: int(clon_vdi.managed), 

1122 METADATA_OF_POOL_TAG: '' 

1123 } 

1124 

1125 if not self.legacyMode: 

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

1127 

1128 except XenAPI.Failure: 

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

1130 

1131 try: 

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

1133 base_vdi.label = "base copy" 

1134 base_vdi.read_only = True 

1135 base_vdi.location = baseUuid 

1136 base_vdi.size = base.sizeVirt 

1137 base_vdi.utilisation = base.sizeLV 

1138 base_vdi.managed = False 

1139 base_vdi.sm_config = { 

1140 "vdi_type": base.vdiType, 

1141 "vhd-parent": baseUuid} 

1142 

1143 if not self.legacyMode: 

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

1145 

1146 base_vdi_ref = base_vdi._db_introduce() 

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

1148 (base_vdi_ref, baseUuid)) 

1149 

1150 vdi_info = {UUID_TAG: baseUuid, 

1151 NAME_LABEL_TAG: base_vdi.label, 

1152 NAME_DESCRIPTION_TAG: base_vdi.description, 

1153 IS_A_SNAPSHOT_TAG: 0, 

1154 SNAPSHOT_OF_TAG: '', 

1155 SNAPSHOT_TIME_TAG: '', 

1156 TYPE_TAG: type, 

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

1158 READ_ONLY_TAG: int(base_vdi.read_only), 

1159 MANAGED_TAG: int(base_vdi.managed), 

1160 METADATA_OF_POOL_TAG: '' 

1161 } 

1162 

1163 if not self.legacyMode: 

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

1165 except XenAPI.Failure: 

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

1167 

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

1169 

1170 def _undoAllJournals(self): 

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

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

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

1174 # ops might require inflations 

1175 self.lock.acquire() 

1176 try: 

1177 self._undoAllInflateJournals() 

1178 self._undoAllCowJournals() 

1179 self._handleInterruptedCloneOps() 

1180 self._handleInterruptedCoalesceLeaf() 

1181 finally: 

1182 self.lock.release() 

1183 self.cleanup() 

1184 

1185 def _undoAllInflateJournals(self): 

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

1187 if len(entries) == 0: 

1188 return 

1189 self._loadvdis() 

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

1191 vdi = self.vdis.get(uuid) 

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

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

1194 (uuid, vdi.path, val)) 

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

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

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

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

1199 

1200 cowutil = getCowUtil(vdi.vdi_type) 

1201 lvmcowutil = LvmCowUtil(cowutil) 

1202 

1203 footer_size = cowutil.getFooterSize() 

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

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

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

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

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

1209 LvmCowUtil.refreshVolumeOnAllSlaves( 

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

1211 ) 

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

1213 delattr(self, "vdiInfo") 

1214 delattr(self, "allVDIs") 

1215 

1216 def _undoAllCowJournals(self): 

1217 """ 

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

1219 """ 

1220 journals = LvmCowUtil.getAllResizeJournals(self.lvmCache) 

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

1222 return 

1223 self._loadvdis() 

1224 

1225 for uuid, jlvName in journals: 

1226 vdi = self.vdis[uuid] 

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

1228 cowutil = getCowUtil(vdi.vdi_type) 

1229 lvmcowutil = LvmCowUtil(cowutil) 

1230 

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

1232 self.lvmCache.activateNoRefcount(jlvName) 

1233 fullSize = lvmcowutil.calcVolumeSize(vdi.size) 

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

1235 try: 

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

1237 cowutil.revert(vdi.path, jFile) 

1238 except util.CommandException: 

1239 util.logException("COW journal revert") 

1240 cowutil.check(vdi.path) 

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

1242 # Attempt to reclaim unused space 

1243 

1244 

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

1246 NewSize = lvmcowutil.calcVolumeSize(imageInfo.sizeVirt) 

1247 if NewSize < fullSize: 

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

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

1250 self.lvmCache.remove(jlvName) 

1251 delattr(self, "vdiInfo") 

1252 delattr(self, "allVDIs") 

1253 

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

1255 master_ref = util.get_this_host_ref(self.session) 

1256 for hostRef in host_refs: 

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

1258 continue 

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

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

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

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

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

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

1265 

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

1267 baseUuid, baseLV): 

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

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

1270 for the base LV""" 

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

1272 "action1": "refresh", 

1273 "lvName1": origLV, 

1274 "action2": "activate", 

1275 "ns2": NS_PREFIX_LVM + self.uuid, 

1276 "lvName2": baseLV, 

1277 "uuid2": baseUuid} 

1278 

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

1280 self.call_on_slave(args, hostRefs, message) 

1281 

1282 def _updateSlavesOnCBTClone(self, hostRefs, cbtlog): 

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

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

1285 "action1": "deactivateNoRefcount", 

1286 "lvName1": cbtlog, 

1287 "action2": "refresh", 

1288 "lvName2": cbtlog} 

1289 

1290 message = f"Updating {cbtlog}" 

1291 self.call_on_slave(args, hostRefs, message) 

1292 

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

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

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

1296 "action1": "cleanupLockAndRefcount", 

1297 "uuid1": baseUuid, 

1298 "ns1": NS_PREFIX_LVM + self.uuid} 

1299 

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

1301 self.call_on_slave(args, hostRefs, message) 

1302 

1303 def _deactivateOnSlave(self, hostRefs, lvname): 

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

1305 args = { 

1306 "vgName": self.vgname, 

1307 "action1": "deactivateNoRefcount", 

1308 "lvName1": lvname} 

1309 

1310 message = f"Deactivating {lvname}" 

1311 self.call_on_slave(args, hostRefs, message) 

1312 

1313 def _cleanup(self, skipLockCleanup=False): 

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

1315 RefCounter.resetAll(NS_PREFIX_LVM + self.uuid) 

1316 IPCFlag(self.uuid).clearAll() 

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

1318 lock.Lock.cleanupAll(self.uuid) 

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

1320 

1321 def _prepareTestMode(self): 

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

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

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

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

1326 

1327 def _kickGC(self): 

1328 util.SMlog("Kicking GC") 

1329 cleanup.start_gc_service(self.uuid) 

1330 

1331 def ensureCBTSpace(self, virtual_size=0): 

1332 # Ensure we have space for at least one LV 

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

1334 self._ensureSpaceAvailable(size) 

1335 

1336 

1337class LVMVDI(VDI.VDI): 

1338 

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

1340 

1341 @override 

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

1343 self.lock = self.sr.lock 

1344 self.lvActivator = self.sr.lvActivator 

1345 self.loaded = False 

1346 if self.sr.legacyMode or util.fistpoint.is_active("xenrt_default_vdi_type_legacy"): 

1347 self._setType(VdiType.RAW) 

1348 self.uuid = vdi_uuid 

1349 self.location = self.uuid 

1350 self.exists = True 

1351 

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

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

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

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

1356 else: 

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

1358 return 

1359 

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

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

1362 return 

1363 

1364 image_format = None 

1365 

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

1367 self.exists = False 

1368 

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

1370 if vdi_sm_config: 

1371 image_format = self.sr.read_config_image_format(vdi_sm_config) 

1372 

1373 if not image_format: 

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

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

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

1377 # We need it to validate the vdi_type choice 

1378 for image_format in self.sr.preferred_image_formats: 

1379 vdi_type = self.sr._resolve_vdi_type_from_image_format(image_format) 

1380 cowutil = getCowUtil(vdi_type) 

1381 try: 

1382 cowutil.validateAndRoundImageSize(size) 

1383 break 

1384 except xs_errors.SROSError: 

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

1386 else: 

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

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

1389 image_format = self.sr.preferred_image_formats[0] 

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

1391 

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

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

1394 

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

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

1397 

1398 @override 

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

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

1401 if not self.sr.isMaster: 

1402 raise xs_errors.XenError('LVMMaster') 

1403 if self.exists: 

1404 raise xs_errors.XenError('VDIExists') 

1405 

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

1407 

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

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

1410 lvSize = 0 

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

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

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

1414 else: 

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

1416 lvSize = util.roundup( 

1417 lvutil.LVM_SIZE_INCREMENT, 

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

1419 ) 

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

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

1422 

1423 self.sr._ensureSpaceAvailable(lvSize) 

1424 

1425 try: 

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

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

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

1429 else: 

1430 self.cowutil.create( 

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

1432 ) 

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

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

1435 except util.CommandException as e: 

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

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

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

1439 

1440 self.utilisation = lvSize 

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

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

1443 

1444 if not self.sr.legacyMode: 

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

1446 

1447 self.ref = self._db_introduce() 

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

1449 

1450 vdi_info = {UUID_TAG: self.uuid, 

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

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

1453 IS_A_SNAPSHOT_TAG: 0, 

1454 SNAPSHOT_OF_TAG: '', 

1455 SNAPSHOT_TIME_TAG: '', 

1456 TYPE_TAG: self.ty, 

1457 VDI_TYPE_TAG: self.vdi_type, 

1458 READ_ONLY_TAG: int(self.read_only), 

1459 MANAGED_TAG: int(self.managed), 

1460 METADATA_OF_POOL_TAG: '' 

1461 } 

1462 

1463 if not self.sr.legacyMode: 

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

1465 

1466 return VDI.VDI.get_params(self) 

1467 

1468 @override 

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

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

1471 try: 

1472 self._loadThis() 

1473 except xs_errors.SRException as e: 

1474 # Catch 'VDI doesn't exist' exception 

1475 if e.errno == 46: 

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

1477 raise 

1478 

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

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

1481 raise xs_errors.XenError("VDIDelete", \ 

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

1483 

1484 if not self.hidden: 

1485 self._markHidden() 

1486 

1487 if not data_only: 

1488 # Remove from XAPI and delete from MGT 

1489 self._db_forget() 

1490 else: 

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

1492 # Only delete from MGT 

1493 if not self.sr.legacyMode: 

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

1495 

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

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

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

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

1500 

1501 try: 

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

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

1504 self.sr.lock.cleanupAll(vdi_uuid) 

1505 except xs_errors.SRException as e: 

1506 util.SMlog( 

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

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

1509 

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

1511 self.sr._kickGC() 

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

1513 

1514 @override 

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

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

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

1518 raise xs_errors.XenError('VDIUnavailable', 

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

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

1521 

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

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

1524 needInflate = True 

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

1526 needInflate = False 

1527 else: 

1528 self._loadThis() 

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

1530 needInflate = False 

1531 

1532 if needInflate: 

1533 try: 

1534 self._prepareThin(True, self.vdi_type) 

1535 except: 

1536 util.logException("attach") 

1537 raise xs_errors.XenError('LVMProvisionAttach') 

1538 

1539 if self.cowutil.isCoalesceableOnRemote(): 

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

1541 self._make_chain_rw() 

1542 

1543 try: 

1544 return self._attach() 

1545 finally: 

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

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

1548 

1549 @override 

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

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

1552 self._loadThis() 

1553 already_deflated = (self.utilisation < \ 

1554 self.lvmcowutil.calcVolumeSize(self.size)) 

1555 needDeflate = True 

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

1557 needDeflate = False 

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

1559 needDeflate = False 

1560 # except for snapshots, which are always deflated 

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

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

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

1564 if snap: 

1565 needDeflate = True 

1566 

1567 if needDeflate: 

1568 try: 

1569 self._prepareThin(False, self.vdi_type) 

1570 except: 

1571 util.logException("_prepareThin") 

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

1573 

1574 try: 

1575 self._detach() 

1576 finally: 

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

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

1579 

1580 # We only support offline resize 

1581 @override 

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

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

1584 if not self.sr.isMaster: 

1585 raise xs_errors.XenError('LVMMaster') 

1586 

1587 self._loadThis() 

1588 if self.hidden: 

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

1590 

1591 if size < self.size: 

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

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

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

1595 

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

1597 

1598 if size == self.size: 

1599 return VDI.VDI.get_params(self) 

1600 

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

1602 lvSizeOld = self.size 

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

1604 else: 

1605 lvSizeOld = self.utilisation 

1606 lvSizeNew = self.lvmcowutil.calcVolumeSize(size) 

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

1608 # VDI is currently deflated, so keep it deflated 

1609 lvSizeNew = lvSizeOld 

1610 assert(lvSizeNew >= lvSizeOld) 

1611 spaceNeeded = lvSizeNew - lvSizeOld 

1612 self.sr._ensureSpaceAvailable(spaceNeeded) 

1613 

1614 oldSize = self.size 

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

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

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

1618 self.utilisation = self.size 

1619 else: 

1620 if lvSizeNew != lvSizeOld: 

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

1622 if self.vdi_type == VdiType.QCOW2: 

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

1624 self._chainSetActive(True, True) 

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

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

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

1628 

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

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

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

1632 str(self.utilisation)) 

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

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

1635 if self.vdi_type == VdiType.QCOW2: 

1636 self._chainSetActive(False, True) 

1637 return VDI.VDI.get_params(self) 

1638 

1639 @override 

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

1641 return self._do_snapshot( 

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

1643 

1644 @override 

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

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

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

1648 raise xs_errors.XenError('Unimplemented') 

1649 

1650 parent_uuid = vdi1 

1651 parent_lvname = LV_PREFIX[self.vdi_type] + parent_uuid 

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

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

1654 

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

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

1657 

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

1659 self.cowutil.setHidden(parent_path) 

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

1661 

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

1663 True): 

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

1665 

1666 util.SMlog("Compose done") 

1667 

1668 def reset_leaf(self, sr_uuid, vdi_uuid): 

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

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

1671 raise xs_errors.XenError('Unimplemented') 

1672 

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

1674 

1675 # safety check 

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

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

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

1679 

1680 self.cowutil.killData(self.path) 

1681 

1682 def _attach(self): 

1683 self._chainSetActive(True, True, True) 

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

1685 raise xs_errors.XenError('VDIUnavailable', \ 

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

1687 

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

1689 self.xenstore_data = {} 

1690 

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

1692 scsiutil.gen_synthetic_page_data(self.uuid))) 

1693 

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

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

1696 

1697 self.attached = True 

1698 self.sr.lvActivator.persist() 

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

1700 

1701 def _detach(self): 

1702 self._chainSetActive(False, True) 

1703 self.attached = False 

1704 

1705 @override 

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

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

1708 # If cbt enabled, save file consistency state 

1709 if cbtlog is not None: 

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

1711 consistency_state = False 

1712 else: 

1713 consistency_state = True 

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

1715 (consistency_state, vdi_uuid)) 

1716 else: 

1717 consistency_state = None 

1718 

1719 pause_time = time.time() 

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

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

1722 

1723 snapResult = None 

1724 try: 

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

1726 except Exception as e1: 

1727 try: 

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

1729 secondary=None) 

1730 except Exception as e2: 

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

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

1733 raise 

1734 self.disable_leaf_on_secondary(vdi_uuid, secondary=secondary) 

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

1736 unpause_time = time.time() 

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

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

1739 (unpause_time - pause_time)) 

1740 return snapResult 

1741 

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

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

1744 

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

1746 raise xs_errors.XenError('LVMMaster') 

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

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

1749 

1750 self._loadThis() 

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

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

1753 

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

1755 

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

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

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

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

1760 raise xs_errors.XenError('Unimplemented', \ 

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

1762 

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

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

1765 self._chainSetActive(True, False) 

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

1767 raise xs_errors.XenError('VDIUnavailable', \ 

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

1769 

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

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

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

1773 raise xs_errors.XenError('VDIUnavailable', \ 

1774 opterr='failed to get COW depth') 

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

1776 raise xs_errors.XenError('SnapshotChainTooLong') 

1777 

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

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

1780 

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

1782 thinpr = util.roundup( 

1783 lvutil.LVM_SIZE_INCREMENT, 

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

1785 ) 

1786 lvSizeOrig = thinpr 

1787 lvSizeClon = thinpr 

1788 

1789 hostRefs = [] 

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

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

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

1793 lvSizeOrig = fullpr 

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

1795 if not self.issnap: 

1796 lvSizeOrig = fullpr 

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

1798 lvSizeClon = fullpr 

1799 

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

1801 snapType == VDI.SNAPSHOT_INTERNAL): 

1802 lvSizeClon = 0 

1803 

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

1805 # inflate journal (for the failure handling 

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

1807 lvSizeBase = self.size 

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

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

1810 size_req -= (self.utilisation - lvSizeBase) 

1811 self.sr._ensureSpaceAvailable(size_req) 

1812 

1813 if hostRefs: 

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

1815 

1816 baseUuid = util.gen_uuid() 

1817 origUuid = self.uuid 

1818 clonUuid = "" 

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

1820 clonUuid = util.gen_uuid() 

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

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

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

1824 

1825 try: 

1826 # self becomes the "base vdi" 

1827 origOldLV = self.lvname 

1828 baseLV = LV_PREFIX[self.vdi_type] + baseUuid 

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

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

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

1832 self.uuid = baseUuid 

1833 self.lvname = baseLV 

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

1835 self.label = "base copy" 

1836 self.read_only = True 

1837 self.location = self.uuid 

1838 self.managed = False 

1839 

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

1841 # the snapshot volumes to avoid requiring double the space 

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

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

1844 self.utilisation = lvSizeBase 

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

1846 

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

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

1849 snapVDI2 = None 

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

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

1852 # If we have CBT enabled on the VDI, 

1853 # set CBT status for the new snapshot disk 

1854 if cbtlog: 

1855 snapVDI2.cbt_enabled = True 

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

1857 

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

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

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

1861 # the parent before we snapshot it 

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

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

1864 else: 

1865 self.cowutil.setHidden(self.path) 

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

1867 

1868 # set the base copy to ReadOnly 

1869 if not self.cowutil.isCoalesceableOnRemote(): 1869 ↛ 1876line 1869 didn't jump to line 1876, because the condition on line 1869 was never false

1870 # For QCOW2, the whole chain need to be 

1871 # writable for tapdisk to do the coalesce. 

1872 # In this case, we don't mark the base copy RO. 

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

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

1875 

1876 if hostRefs: 

1877 self.sr._updateSlavesOnClone(hostRefs, origOldLV, 

1878 snapVDI.lvname, self.uuid, self.lvname) 

1879 

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

1881 if snapType == VDI.SNAPSHOT_DOUBLE and cbtlog: 

1882 snapVDI._cbt_snapshot(clonUuid, cbt_consistency) 

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

1884 cbtlog_file = self._get_cbt_logname(snapVDI.uuid) 

1885 try: 

1886 self.sr._updateSlavesOnCBTClone(hostRefs, cbtlog_file) 

1887 except: 

1888 alert_name = "VDI_CBT_SNAPSHOT_FAILED" 

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

1890 .format(snapVDI.uuid)) 

1891 snapVDI._disable_cbt_on_error(alert_name, alert_str) 

1892 pass 

1893 

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

1895 util.logException("LVMVDI._snapshot") 

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

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

1898 

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

1900 

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

1902 

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

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

1905 

1906 snapLV = LV_PREFIX[snapVdiType] + snapUuid 

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

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

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

1910 if isNew: 

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

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

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

1914 self.cowutil.snapshot( 

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

1916 ) 

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

1918 

1919 snapVDI = LVMVDI(self.sr, snapUuid) 

1920 snapVDI.read_only = False 

1921 snapVDI.location = snapUuid 

1922 snapVDI.size = self.size 

1923 snapVDI.utilisation = snapSizeLV 

1924 snapVDI.sm_config = dict() 

1925 for key, val in self.sm_config.items(): 

1926 if key not in [ 1926 ↛ 1929line 1926 didn't jump to line 1929, because the condition on line 1926 was never true

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

1928 not key.startswith("host_"): 

1929 snapVDI.sm_config[key] = val 

1930 snapVDI.sm_config["vdi_type"] = snapVdiType 

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

1932 # TODO: fix the raw snapshot case  

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

1934 snapVDI.lvname = snapLV 

1935 return snapVDI 

1936 

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

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

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

1940 basePresent = True 

1941 

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

1943 # unused 

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

1945 snap2Parent = "" 

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

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

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

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

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

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

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

1953 

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

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

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

1957 if hostRefs: 

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

1959 

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

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

1962 if hostRefs: 

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

1964 basePresent = False 

1965 else: 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1982 ns = NS_PREFIX_LVM + self.sr.uuid 

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

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

1985 

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

1987 # the leaf without being inherited by anyone else 

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

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

1990 del self.sm_config[key] 

1991 

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

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

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

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

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

1997 vdiRef = snapVDI2._db_introduce() 

1998 if cloneOp: 

1999 vdi_info = {UUID_TAG: snapVDI2.uuid, 

2000 NAME_LABEL_TAG: util.to_plain_string( \ 

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

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

2003 NAME_DESCRIPTION_TAG: util.to_plain_string( \ 

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

2005 IS_A_SNAPSHOT_TAG: 0, 

2006 SNAPSHOT_OF_TAG: '', 

2007 SNAPSHOT_TIME_TAG: '', 

2008 TYPE_TAG: type, 

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

2010 READ_ONLY_TAG: 0, 

2011 MANAGED_TAG: int(snapVDI2.managed), 

2012 METADATA_OF_POOL_TAG: '' 

2013 } 

2014 else: 

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

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

2017 vdi_info = {UUID_TAG: snapVDI2.uuid, 

2018 NAME_LABEL_TAG: util.to_plain_string( \ 

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

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

2021 NAME_DESCRIPTION_TAG: util.to_plain_string( \ 

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

2023 IS_A_SNAPSHOT_TAG: 1, 

2024 SNAPSHOT_OF_TAG: snapVDI.uuid, 

2025 SNAPSHOT_TIME_TAG: '', 

2026 TYPE_TAG: type, 

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

2028 READ_ONLY_TAG: 0, 

2029 MANAGED_TAG: int(snapVDI2.managed), 

2030 METADATA_OF_POOL_TAG: '' 

2031 } 

2032 

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

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

2035 (vdiRef, snapVDI2.uuid)) 

2036 

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

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

2039 vdiRef = self._db_introduce() 

2040 vdi_info = {UUID_TAG: self.uuid, 

2041 NAME_LABEL_TAG: self.label, 

2042 NAME_DESCRIPTION_TAG: self.description, 

2043 IS_A_SNAPSHOT_TAG: 0, 

2044 SNAPSHOT_OF_TAG: '', 

2045 SNAPSHOT_TIME_TAG: '', 

2046 TYPE_TAG: type, 

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

2048 READ_ONLY_TAG: 1, 

2049 MANAGED_TAG: 0, 

2050 METADATA_OF_POOL_TAG: '' 

2051 } 

2052 

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

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

2055 (vdiRef, self.uuid)) 

2056 

2057 # Update the original record 

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

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

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

2061 str(snapVDI.utilisation)) 

2062 

2063 # Return the info on the new snap VDI 

2064 snap = snapVDI2 

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

2066 snap = self 

2067 if not basePresent: 

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

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

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

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

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

2073 snap = snapVDI 

2074 return snap.get_params() 

2075 

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

2077 self.vdi_type = vdiType 

2078 self.cowutil = getCowUtil(self.vdi_type) 

2079 self.lvmcowutil = LvmCowUtil(self.cowutil) 

2080 

2081 def _initFromVDIInfo(self, vdiInfo): 

2082 self._setType(vdiInfo.vdiType) 

2083 self.lvname = vdiInfo.lvName 

2084 self.size = vdiInfo.sizeVirt 

2085 self.utilisation = vdiInfo.sizeLV 

2086 self.hidden = vdiInfo.hidden 

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

2088 self.managed = False 

2089 self.active = vdiInfo.lvActive 

2090 self.readonly = vdiInfo.lvReadonly 

2091 self.parent = vdiInfo.parentUuid 

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

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

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

2095 else: 

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

2097 self.loaded = True 

2098 

2099 def _initFromLVInfo(self, lvInfo): 

2100 self._setType(lvInfo.vdiType) 

2101 self.lvname = lvInfo.name 

2102 self.size = lvInfo.size 

2103 self.utilisation = lvInfo.size 

2104 self.hidden = lvInfo.hidden 

2105 self.active = lvInfo.active 

2106 self.readonly = lvInfo.readonly 

2107 self.parent = '' 

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

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

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

2111 else: 

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

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

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

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

2116 self.loaded = True 

2117 

2118 def _initFromImageInfo(self, imageInfo): 

2119 self.size = imageInfo.sizeVirt 

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

2121 self.parent = imageInfo.parentUuid 

2122 self.hidden = imageInfo.hidden 

2123 self.loaded = True 

2124 

2125 def _determineType(self): 

2126 """ 

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

2128 """ 

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

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

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

2132 if sm_config.get("vdi_type"): 

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

2134 prefix = LV_PREFIX[self.vdi_type] 

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

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

2137 self.sm_config_override = sm_config 

2138 return True 

2139 

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

2141 # the LV is active 

2142 found = False 

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

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

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

2146 if util.pathexists(path): 

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

2148 raise xs_errors.XenError('VDILoad', 

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

2150 found = True 

2151 self._setType(vdi_type) 

2152 self.lvname = lvname 

2153 self.path = path 

2154 if found: 

2155 return True 

2156 

2157 # now list all LV's 

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

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

2160 return False 

2161 

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

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

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

2165 return True 

2166 return False 

2167 

2168 def _loadThis(self): 

2169 """ 

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

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

2172 """ 

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

2174 if VdiType.isCowImage(self.vdi_type): 

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

2176 return 

2177 try: 

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

2179 except util.CommandException as e: 

2180 raise xs_errors.XenError('VDIUnavailable', 

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

2182 if not lvs.get(self.uuid): 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='LV not found') 

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

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

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

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

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

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

2190 self._initFromImageInfo(imageInfo) 

2191 self.loaded = True 

2192 

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

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

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

2196 NS_PREFIX_LVM + self.sr.uuid) 

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

2198 return # this is a redundant activation/deactivation call 

2199 

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

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

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

2203 for uuid, lvName in vdiList.items(): 

2204 binaryParam = binary 

2205 if uuid != self.uuid: 2205 ↛ 2206line 2205 didn't jump to line 2206, because the condition on line 2205 was never true

2206 binaryParam = False # binary param only applies to leaf nodes 

2207 if active: 2207 ↛ 2214line 2207 didn't jump to line 2214, because the condition on line 2207 was never false

2208 self.sr.lvActivator.activate(uuid, lvName, binaryParam, 

2209 persistent) 

2210 else: 

2211 # just add the LVs for deactivation in the final (cleanup) 

2212 # step. The LVs must not have been activated during the current 

2213 # operation 

2214 self.sr.lvActivator.add(uuid, lvName, binaryParam) 

2215 

2216 def _failClone(self, uuid, jval, msg): 

2217 try: 

2218 self.sr._handleInterruptedCloneOp(uuid, jval, True) 

2219 self.sr.journaler.remove(self.JRN_CLONE, uuid) 

2220 except Exception as e: 

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

2222 ' %s (error ignored)' % e) 

2223 raise xs_errors.XenError('VDIClone', opterr=msg) 

2224 

2225 def _markHidden(self): 

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

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

2228 else: 

2229 self.cowutil.setHidden(self.path) 

2230 self.hidden = 1 

2231 

2232 def _prepareThin(self, attach, vdiType): 

2233 origUtilisation = self.sr.lvmCache.getSize(self.lvname) 

2234 if self.sr.isMaster: 

2235 # the master can prepare the VDI locally 

2236 if attach: 

2237 self.lvmcowutil.attachThin(self.sr.journaler, self.sr.uuid, self.uuid, self.vdi_type) 

2238 else: 

2239 self.lvmcowutil.detachThin(self.session, self.sr.lvmCache, self.sr.uuid, self.uuid, self.vdi_type) 

2240 else: 

2241 fn = "attach" 

2242 if not attach: 

2243 fn = "detach" 

2244 pools = self.session.xenapi.pool.get_all() 

2245 master = self.session.xenapi.pool.get_master(pools[0]) 

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

2247 master, 

2248 self.sr.THIN_PLUGIN, 

2249 fn, 

2250 { 

2251 "srUuid": self.sr.uuid, 

2252 "vdiUuid": self.uuid, 

2253 "vdiType": vdiType 

2254 } 

2255 ) 

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

2257 if not rv: 

2258 raise Exception('plugin %s failed' % self.sr.THIN_PLUGIN) 

2259 # refresh to pick up the size change on this slave 

2260 self.sr.lvmCache.activateNoRefcount(self.lvname, True) 

2261 

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

2263 if origUtilisation != self.utilisation: 

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

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

2266 str(self.utilisation)) 

2267 stats = lvutil._getVGstats(self.sr.vgname) 

2268 sr_utilisation = stats['physical_utilisation'] 

2269 self.session.xenapi.SR.set_physical_utilisation(self.sr.sr_ref, 

2270 str(sr_utilisation)) 

2271 

2272 def _make_chain_rw(self): 

2273 """ 

2274 In the case of QCOW2, we need the chain to be RW for the coalesce to be done by tapdisk. 

2275 Setting the chain to be RW early avoids having to stop the tapdisk so LVM can acknowledge the chain RW on the slave. 

2276 And while making the change to RW from the slave works without the refresh, it can cause LVM metadata corruption. 

2277 """ 

2278 # TODO(XCPNG-3689): We could check if any VDI on the chain is RO before doing the call to master to change this 

2279 if self.sr.isMaster: 

2280 lvmcache = self.sr.lvmCache 

2281 for uuid, lvName in self.cowutil.getParentChain(self.lvname, LvmCowUtil.extractUuid, self.sr.vgname).items(): 

2282 lvmcache.setReadonly(lvName, False) 

2283 else: 

2284 master = util.get_master_ref(self.session) 

2285 response = self.session.xenapi.host.call_plugin( 

2286 master, 

2287 self.sr.PLUGIN_ON_SLAVE, 

2288 "make_chain_rw", 

2289 { 

2290 "vgName": self.sr.vgname, 

2291 "lvName": self.lvname, 

2292 "vdiType": self.vdi_type 

2293 } 

2294 ) 

2295 util.SMlog(f"call-plugin returned: {response}") 

2296 if not response: 

2297 raise Exception(f"plugin {self.sr.PLUGIN_ON_SLAVE} failed") 

2298 

2299 @override 

2300 def update(self, sr_uuid, vdi_uuid) -> None: 

2301 if self.sr.legacyMode: 

2302 return 

2303 

2304 #Synch the name_label of this VDI on storage with the name_label in XAPI 

2305 vdi_ref = self.session.xenapi.VDI.get_by_uuid(self.uuid) 

2306 update_map = {} 

2307 update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] = \ 

2308 METADATA_OBJECT_TYPE_VDI 

2309 update_map[UUID_TAG] = self.uuid 

2310 update_map[NAME_LABEL_TAG] = util.to_plain_string( \ 

2311 self.session.xenapi.VDI.get_name_label(vdi_ref)) 

2312 update_map[NAME_DESCRIPTION_TAG] = util.to_plain_string( \ 

2313 self.session.xenapi.VDI.get_name_description(vdi_ref)) 

2314 update_map[SNAPSHOT_TIME_TAG] = \ 

2315 self.session.xenapi.VDI.get_snapshot_time(vdi_ref) 

2316 update_map[METADATA_OF_POOL_TAG] = \ 

2317 self.session.xenapi.VDI.get_metadata_of_pool(vdi_ref) 

2318 LVMMetadataHandler(self.sr.mdpath).updateMetadata(update_map) 

2319 

2320 @override 

2321 def _ensure_cbt_space(self) -> None: 

2322 # We need virtual_size to compute the size in case of a bigger VDI 

2323 self.sr.ensureCBTSpace(self.size) 

2324 

2325 @override 

2326 def _create_cbt_log(self) -> str: 

2327 logname = self._get_cbt_logname(self.uuid) 

2328 logsize = max(util.roundup(CBT_BLOCK_SIZE, self.size//CBT_BLOCK_SIZE), self.sr.journaler.LV_SIZE) 

2329 # We choose 4MiB as the minimum for the log size to maintain the old behavior and compute the correct amount 

2330 # if we need a bigger LV for the CBT (can happen with big QCOW2) 

2331 self.sr.lvmCache.create(logname, logsize, CBTLOG_TAG) 

2332 logpath = super(LVMVDI, self)._create_cbt_log() 

2333 self.sr.lvmCache.deactivateNoRefcount(logname) 

2334 return logpath 

2335 

2336 @override 

2337 def _delete_cbt_log(self) -> None: 

2338 logpath = self._get_cbt_logpath(self.uuid) 

2339 if not self._cbt_log_exists(logpath): 

2340 return 

2341 logname = self._get_cbt_logname(self.uuid) 

2342 if not self.sr.isMaster and self.sr.is_shared(): 

2343 master = util.get_master_ref(self.session) 

2344 response = self.session.xenapi.host.call_plugin( 

2345 master, 

2346 self.sr.PLUGIN_ON_MASTER, 

2347 "delete_cbt_log", 

2348 { 

2349 "vgName": self.sr.vgname, 

2350 "lvName": logname, 

2351 } 

2352 ) 

2353 util.SMlog(f"on-master.delete_cbt_log call-plugin returned: {response}") 

2354 if not response: 

2355 raise Exception(f"plugin {self.sr.PLUGIN_ON_MASTER} failed") 

2356 lvutil._lvmBugCleanup(logpath) 

2357 return 

2358 self.sr.lvmCache.remove(logname) 

2359 

2360 @override 

2361 def _rename(self, oldpath, newpath) -> None: 

2362 oldname = os.path.basename(oldpath) 

2363 newname = os.path.basename(newpath) 

2364 self.sr.lvmCache.rename(oldname, newname) 

2365 

2366 @override 

2367 def update_slaves_on_cbt_disable(self, cbtlog) -> None: 

2368 args = { 

2369 "vgName": self.sr.vgname, 

2370 "action1": "deactivateNoRefcount", 

2371 "lvName1": cbtlog 

2372 } 

2373 

2374 host_refs = util.get_hosts_attached_on(self.session, [self.uuid]) 

2375 

2376 message = f"Deactivating {cbtlog}" 

2377 self.sr.call_on_slave(args, host_refs, message) 

2378 

2379 @override 

2380 def _activate_cbt_log(self, lv_name) -> bool: 

2381 self.sr.lvmCache.refresh() 

2382 if not self.sr.lvmCache.is_active(lv_name): 2382 ↛ 2383line 2382 didn't jump to line 2383, because the condition on line 2382 was never true

2383 try: 

2384 self.sr.lvmCache.activateNoRefcount(lv_name) 

2385 return True 

2386 except Exception as e: 

2387 util.SMlog("Exception in _activate_cbt_log, " 

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

2389 raise 

2390 else: 

2391 return False 

2392 

2393 @override 

2394 def _deactivate_cbt_log(self, lv_name) -> None: 

2395 try: 

2396 self.sr.lvmCache.deactivateNoRefcount(lv_name) 

2397 except Exception as e: 

2398 util.SMlog("Exception in _deactivate_cbt_log, Error: %s." % str(e)) 

2399 raise 

2400 

2401 @override 

2402 def _cbt_log_exists(self, logpath) -> bool: 

2403 return lvutil.exists(logpath) 

2404 

2405if __name__ == '__main__': 2405 ↛ 2406line 2405 didn't jump to line 2406, because the condition on line 2405 was never true

2406 SRCommand.run(LVMSR, DRIVER_INFO) 

2407else: 

2408 SR.registerSR(LVMSR)