Coverage for libs/sm/drivers/LVHDSR.py: 47%

1370 statements  

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

1# Copyright (C) Citrix Systems Inc. 

2# 

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

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

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

6# 

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

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

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

10# GNU Lesser General Public License for more details. 

11# 

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

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

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

15# 

16# LVHDSR: VHD on LVM storage repository 

17# matches with drivers/LVHDSR 

18# 

19import os 

20import sys 

21import time 

22import errno 

23import re 

24import glob 

25 

26from sm import SR 

27from sm.SR import deviceCheck 

28from sm import VDI 

29from sm.core import util 

30from sm.core import scsiutil 

31from sm.core import xs_errors 

32from sm.core.lock import Lock 

33from sm import lvutil 

34from sm import cleanup 

35from sm import lvmcache 

36from sm import vhdutil 

37from sm import lvhdutil 

38from sm import blktap2 

39from sm.journaler import Journaler 

40from sm.refcounter import RefCounter 

41from sm.ipc import IPCFlag 

42from sm.lvmanager import LVActivator 

43import XenAPI # pylint: disable=import-error 

44from sm.srmetadata import ALLOCATION_TAG, NAME_LABEL_TAG, NAME_DESCRIPTION_TAG, \ 

45 UUID_TAG, IS_A_SNAPSHOT_TAG, SNAPSHOT_OF_TAG, TYPE_TAG, VDI_TYPE_TAG, \ 

46 READ_ONLY_TAG, MANAGED_TAG, SNAPSHOT_TIME_TAG, METADATA_OF_POOL_TAG, \ 

47 LVMMetadataHandler, METADATA_OBJECT_TYPE_VDI, \ 

48 METADATA_OBJECT_TYPE_SR, METADATA_UPDATE_OBJECT_TYPE_TAG 

49from sm.metadata import retrieveXMLfromFile, _parseXML 

50from xmlrpc.client import DateTime 

51from sm.constants import CBTLOG_TAG 

52from fairlock import Fairlock 

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

54 

55geneology = {} 

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

57 "VDI_CREATE", "VDI_DELETE", "VDI_ATTACH", "VDI_DETACH", "VDI_MIRROR", 

58 "VDI_CLONE", "VDI_SNAPSHOT", "VDI_RESIZE", "ATOMIC_PAUSE", 

59 "VDI_RESET_ON_BOOT/2", "VDI_UPDATE", "VDI_CONFIG_CBT", 

60 "VDI_ACTIVATE", "VDI_DEACTIVATE"] 

61 

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

63 

64DRIVER_INFO = { 

65 'name': 'Local VHD on LVM', 

66 'description': 'SR plugin which represents disks as VHD disks on ' + \ 

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

68 'vendor': 'XenSource Inc', 

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

70 'driver_version': '1.0', 

71 'required_api_version': '1.0', 

72 'capabilities': CAPABILITIES, 

73 'configuration': CONFIGURATION 

74 } 

75 

76PARAM_VHD = "vhd" 

77PARAM_RAW = "raw" 

78 

79OPS_EXCLUSIVE = [ 

80 "sr_create", "sr_delete", "sr_attach", "sr_detach", "sr_scan", 

81 "sr_update", "vdi_create", "vdi_delete", "vdi_resize", "vdi_snapshot", 

82 "vdi_clone"] 

83 

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

85LONG_SNAPTIME = 60 

86 

87class LVHDSR(SR.SR): 

88 DRIVER_TYPE = 'lvhd' 

89 

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

91 PROVISIONING_DEFAULT = "thick" 

92 THIN_PLUGIN = "lvhd-thin" 

93 

94 PLUGIN_ON_SLAVE = "on-slave" 

95 

96 FLAG_USE_VHD = "use_vhd" 

97 MDVOLUME_NAME = "MGT" 

98 

99 ALLOCATION_QUANTUM = "allocation_quantum" 

100 INITIAL_ALLOCATION = "initial_allocation" 

101 

102 LOCK_RETRY_INTERVAL = 3 

103 LOCK_RETRY_ATTEMPTS = 10 

104 

105 TEST_MODE_KEY = "testmode" 

106 TEST_MODE_VHD_FAIL_REPARENT_BEGIN = "vhd_fail_reparent_begin" 

107 TEST_MODE_VHD_FAIL_REPARENT_LOCATOR = "vhd_fail_reparent_locator" 

108 TEST_MODE_VHD_FAIL_REPARENT_END = "vhd_fail_reparent_end" 

109 TEST_MODE_VHD_FAIL_RESIZE_BEGIN = "vhd_fail_resize_begin" 

110 TEST_MODE_VHD_FAIL_RESIZE_DATA = "vhd_fail_resize_data" 

111 TEST_MODE_VHD_FAIL_RESIZE_METADATA = "vhd_fail_resize_metadata" 

112 TEST_MODE_VHD_FAIL_RESIZE_END = "vhd_fail_resize_end" 

113 

114 ENV_VAR_VHD_TEST = { 

115 TEST_MODE_VHD_FAIL_REPARENT_BEGIN: 

116 "VHD_UTIL_TEST_FAIL_REPARENT_BEGIN", 

117 TEST_MODE_VHD_FAIL_REPARENT_LOCATOR: 

118 "VHD_UTIL_TEST_FAIL_REPARENT_LOCATOR", 

119 TEST_MODE_VHD_FAIL_REPARENT_END: 

120 "VHD_UTIL_TEST_FAIL_REPARENT_END", 

121 TEST_MODE_VHD_FAIL_RESIZE_BEGIN: 

122 "VHD_UTIL_TEST_FAIL_RESIZE_BEGIN", 

123 TEST_MODE_VHD_FAIL_RESIZE_DATA: 

124 "VHD_UTIL_TEST_FAIL_RESIZE_DATA_MOVED", 

125 TEST_MODE_VHD_FAIL_RESIZE_METADATA: 

126 "VHD_UTIL_TEST_FAIL_RESIZE_METADATA_MOVED", 

127 TEST_MODE_VHD_FAIL_RESIZE_END: 

128 "VHD_UTIL_TEST_FAIL_RESIZE_END" 

129 } 

130 testMode = "" 

131 

132 legacyMode = True 

133 

134 @staticmethod 

135 def handles(type): 

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

137 # we can pose as LVMSR for compatibility purposes 

138 if type == "lvm": 

139 return True 

140 return type == LVHDSR.DRIVER_TYPE 

141 

142 def load(self, sr_uuid): 

143 self.ops_exclusive = OPS_EXCLUSIVE 

144 

145 self.isMaster = False 

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

147 self.isMaster = True 

148 

149 self.lock = Lock(vhdutil.LOCK_TYPE_SR, self.uuid) 

150 self.sr_vditype = SR.DEFAULT_TAP 

151 self.uuid = sr_uuid 

152 self.vgname = lvhdutil.VG_PREFIX + self.uuid 

153 self.path = os.path.join(lvhdutil.VG_LOCATION, self.vgname) 

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

155 self.provision = self.PROVISIONING_DEFAULT 

156 try: 

157 self.lvmCache = lvmcache.LVMCache(self.vgname) 

158 except: 

159 raise xs_errors.XenError('SRUnavailable', \ 

160 opterr='Failed to initialise the LVMCache') 

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

162 self.journaler = Journaler(self.lvmCache) 

163 if not self.srcmd.params.get("sr_ref"): 

164 return # must be a probe call 

165 # Test for thick vs thin provisioning conf parameter 

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

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

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

169 else: 

170 raise xs_errors.XenError('InvalidArg', \ 

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

172 

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

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

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

176 self._prepareTestMode() 

177 

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

179 # sm_config flag overrides PBD, if any 

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

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

182 

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

184 self.legacyMode = False 

185 

186 if lvutil._checkVG(self.vgname): 

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

188 "vdi_activate", "vdi_deactivate"]: 

189 self._undoAllJournals() 

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

191 self._checkMetadataVolume() 

192 

193 self.mdexists = False 

194 

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

196 contains_uuid_regex = \ 

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

198 self.storageVDIs = {} 

199 

200 vdi = None 

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

202 # if the lvname has a uuid in it 

203 type = None 

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

205 if key.startswith(lvhdutil.LV_PREFIX[vhdutil.VDI_TYPE_VHD]): 

206 type = vhdutil.VDI_TYPE_VHD 

207 vdi = key[len(lvhdutil.LV_PREFIX[type]):] 

208 elif key.startswith(lvhdutil.LV_PREFIX[vhdutil.VDI_TYPE_RAW]): 

209 type = vhdutil.VDI_TYPE_RAW 

210 vdi = key[len(lvhdutil.LV_PREFIX[type]):] 

211 else: 

212 continue 

213 

214 if type is not None: 

215 self.storageVDIs[vdi] = type 

216 

217 # check if metadata volume exists 

218 try: 

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

220 except: 

221 pass 

222 

223 def cleanup(self): 

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

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

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

227 

228 def updateSRMetadata(self, allocation): 

229 try: 

230 # Add SR specific SR metadata 

231 sr_info = \ 

232 {ALLOCATION_TAG: allocation, 

233 UUID_TAG: self.uuid, 

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

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

236 } 

237 

238 vdi_info = {} 

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

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

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

242 

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

244 if not vdi_type: 

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

246 

247 # Create the VDI entry in the SR metadata 

248 vdi_info[vdi_uuid] = \ 

249 { 

250 UUID_TAG: vdi_uuid, 

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

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

253 IS_A_SNAPSHOT_TAG: \ 

254 is_a_snapshot, 

255 SNAPSHOT_OF_TAG: \ 

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

257 SNAPSHOT_TIME_TAG: \ 

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

259 TYPE_TAG: \ 

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

261 VDI_TYPE_TAG: \ 

262 vdi_type, 

263 READ_ONLY_TAG: \ 

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

265 METADATA_OF_POOL_TAG: \ 

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

267 MANAGED_TAG: \ 

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

269 } 

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

271 

272 except Exception as e: 

273 raise xs_errors.XenError('MetadataError', \ 

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

275 

276 def syncMetadataAndStorage(self): 

277 try: 

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

279 # then delete it from the metadata 

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

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

282 update_map = {} 

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

284 # delete this from metadata 

285 LVMMetadataHandler(self.mdpath). \ 

286 deleteVdiFromMetadata(vdi_info[vdi][UUID_TAG]) 

287 else: 

288 # search for this in the metadata, compare types 

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

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

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

292 # storage type takes authority 

293 update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] \ 

294 = METADATA_OBJECT_TYPE_VDI 

295 update_map[UUID_TAG] = vdi_info[vdi][UUID_TAG] 

296 update_map[VDI_TYPE_TAG] = \ 

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

298 LVMMetadataHandler(self.mdpath) \ 

299 .updateMetadata(update_map) 

300 else: 

301 # This should never happen 

302 pass 

303 

304 except Exception as e: 

305 raise xs_errors.XenError('MetadataError', \ 

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

307 

308 def syncMetadataAndXapi(self): 

309 try: 

310 # get metadata 

311 (sr_info, vdi_info) = \ 

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

313 

314 # First synch SR parameters 

315 self.update(self.uuid) 

316 

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

318 for vdi_offset in vdi_info.keys(): 

319 try: 

320 vdi_ref = \ 

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

322 vdi_info[vdi_offset][UUID_TAG]) 

323 except: 

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

325 continue 

326 

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

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

329 

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

331 vdi_info[vdi_offset][NAME_DESCRIPTION_TAG] != \ 

332 new_name_description: 

333 update_map = {} 

334 update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] = \ 

335 METADATA_OBJECT_TYPE_VDI 

336 update_map[UUID_TAG] = vdi_info[vdi_offset][UUID_TAG] 

337 update_map[NAME_LABEL_TAG] = new_name_label 

338 update_map[NAME_DESCRIPTION_TAG] = new_name_description 

339 LVMMetadataHandler(self.mdpath) \ 

340 .updateMetadata(update_map) 

341 except Exception as e: 

342 raise xs_errors.XenError('MetadataError', \ 

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

344 

345 def _checkMetadataVolume(self): 

346 util.SMlog("Entering _checkMetadataVolume") 

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

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

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

350 try: 

351 # activate the management volume 

352 # will be deactivated at detach time 

353 self.lvmCache.activateNoRefcount(self.MDVOLUME_NAME) 

354 self._synchSmConfigWithMetaData() 

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

356 self.syncMetadataAndStorage() 

357 self.syncMetadataAndXapi() 

358 except Exception as e: 

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

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

361 elif not self.mdexists and not self.legacyMode: 361 ↛ 364line 361 didn't jump to line 364, because the condition on line 361 was never false

362 self._introduceMetaDataVolume() 

363 

364 if self.mdexists: 

365 self.legacyMode = False 

366 

367 def _synchSmConfigWithMetaData(self): 

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

369 

370 try: 

371 # get SR info from metadata 

372 sr_info = {} 

373 map = {} 

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

375 

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

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

378 

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

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

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

382 else: 

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

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

385 

386 except Exception as e: 

387 raise xs_errors.XenError( 

388 'MetadataError', 

389 opterr='Error reading SR params from ' 

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

391 try: 

392 map[self.FLAG_USE_VHD] = 'true' 

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

394 except: 

395 raise xs_errors.XenError( 

396 'MetadataError', 

397 opterr='Error updating sm_config key') 

398 

399 def _introduceMetaDataVolume(self): 

400 util.SMlog("Creating Metadata volume") 

401 try: 

402 config = {} 

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

404 

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

406 self.lvmCache.activateNoRefcount(self.MDVOLUME_NAME) 

407 

408 name_label = util.to_plain_string( \ 

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

410 name_description = util.to_plain_string( \ 

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

412 config[self.FLAG_USE_VHD] = "true" 

413 config['allocation'] = self.provision 

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

415 

416 # Add the SR metadata 

417 self.updateSRMetadata(self.provision) 

418 except Exception as e: 

419 raise xs_errors.XenError('MetadataError', \ 

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

421 

422 def _removeMetadataVolume(self): 

423 if self.mdexists: 

424 try: 

425 self.lvmCache.remove(self.MDVOLUME_NAME) 

426 except: 

427 raise xs_errors.XenError('MetadataError', \ 

428 opterr='Failed to delete MGT Volume') 

429 

430 def _refresh_size(self): 

431 """ 

432 Refreshs the size of the backing device. 

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

434 """ 

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

436 # LVHDoHBASR, LVHDoISCSISR 

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

438 else: 

439 # LVHDSR 

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

441 scsiutil.refreshdev(devices) 

442 return True 

443 

444 def _expand_size(self): 

445 """ 

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

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

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

449 """ 

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

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

452 resizethreshold = 100 * 1024 * 1024 # 100MB 

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

454 totaldevicesize = 0 

455 for device in devices: 

456 totaldevicesize = totaldevicesize + scsiutil.getsize(device) 

457 if totaldevicesize >= (currentvgsize + resizethreshold): 

458 try: 

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

460 # LVHDoHBASR, LVHDoISCSISR might have slaves 

461 scsiutil.refresh_lun_size_by_SCSIid_on_slaves(self.session, 

462 getattr(self, 'SCSIid')) 

463 util.SMlog("LVHDSR._expand_size for %s will resize the pv." % 

464 self.uuid) 

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

466 lvutil.resizePV(pv) 

467 except: 

468 util.logException("LVHDSR._expand_size for %s failed to resize" 

469 " the PV" % self.uuid) 

470 

471 @deviceCheck 

472 def create(self, uuid, size): 

473 util.SMlog("LVHDSR.create for %s" % self.uuid) 

474 if not self.isMaster: 

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

476 raise xs_errors.XenError('LVMMaster') 

477 

478 if lvutil._checkVG(self.vgname): 

479 raise xs_errors.XenError('SRExists') 

480 

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

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

483 raise xs_errors.XenError('SRInUse') 

484 

485 # Check serial number entry in SR records 

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

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

488 raise xs_errors.XenError('SRInUse') 

489 

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

491 

492 #Update serial number string 

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

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

495 

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

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

498 self.FLAG_USE_VHD, 'true') 

499 

500 def delete(self, uuid): 

501 util.SMlog("LVHDSR.delete for %s" % self.uuid) 

502 if not self.isMaster: 

503 raise xs_errors.XenError('LVMMaster') 

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

505 

506 success = True 

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

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

509 continue 

510 

511 if util.doesFileHaveOpenHandles(fileName): 

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

513 "handles" % fileName) 

514 success = False 

515 continue 

516 

517 # Now attempt to remove the dev mapper entry 

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

519 success = False 

520 continue 

521 

522 try: 

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

524 replace('//', '-')) 

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

526 os.unlink(lpath) 

527 except OSError as e: 

528 if e.errno != errno.ENOENT: 

529 util.SMlog("LVHDSR.delete: failed to remove the symlink for " \ 

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

531 success = False 

532 

533 if success: 

534 try: 

535 if util.pathexists(self.path): 

536 os.rmdir(self.path) 

537 except Exception as e: 

538 util.SMlog("LVHDSR.delete: failed to remove the symlink " \ 

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

540 success = False 

541 

542 self._removeMetadataVolume() 

543 self.lvmCache.refresh() 

544 if len(lvhdutil.getLVInfo(self.lvmCache)) > 0: 

545 raise xs_errors.XenError('SRNotEmpty') 

546 

547 if not success: 

548 raise Exception("LVHDSR delete failed, please refer to the log " \ 

549 "for details.") 

550 

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

552 self._cleanup() 

553 

554 def attach(self, uuid): 

555 util.SMlog("LVHDSR.attach for %s" % self.uuid) 

556 

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

558 

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

560 raise xs_errors.XenError('SRUnavailable', \ 

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

562 

563 # Refresh the metadata status 

564 self._checkMetadataVolume() 

565 

566 refreshsizeok = self._refresh_size() 

567 

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

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

570 self._expand_size() 

571 

572 # Update SCSIid string 

573 util.SMlog("Calling devlist_to_serial") 

574 scsiutil.add_serial_record( 

575 self.session, self.sr_ref, 

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

577 

578 # Test Legacy Mode Flag and update if VHD volumes exist 

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

580 vdiInfo = lvhdutil.getVDIInfo(self.lvmCache) 

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

582 if info.vdiType == vhdutil.VDI_TYPE_VHD: 

583 self.legacyMode = False 

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

585 self._introduceMetaDataVolume() 

586 break 

587 

588 # Set the block scheduler 

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

590 self.block_setscheduler(dev) 

591 

592 def detach(self, uuid): 

593 util.SMlog("LVHDSR.detach for %s" % self.uuid) 

594 cleanup.abort(self.uuid) 

595 

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

597 # go through all devmapper entries for this VG 

598 success = True 

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

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

601 continue 

602 

603 with Fairlock('devicemapper'): 

604 # check if any file has open handles 

605 if util.doesFileHaveOpenHandles(fileName): 

606 # if yes, log this and signal failure 

607 util.SMlog( 

608 f"LVHDSR.detach: The dev mapper entry {fileName} has " 

609 "open handles") 

610 success = False 

611 continue 

612 

613 # Now attempt to remove the dev mapper entry 

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

615 success = False 

616 continue 

617 

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

619 try: 

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

621 replace('//', '-')) 

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

623 util.force_unlink(lvname) 

624 except Exception as e: 

625 util.SMlog("LVHDSR.detach: failed to remove the symlink for " \ 

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

627 success = False 

628 

629 # now remove the directory where the symlinks are 

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

631 if success: 

632 try: 

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

634 os.rmdir(self.path) 

635 except Exception as e: 

636 util.SMlog("LVHDSR.detach: failed to remove the symlink " \ 

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

638 success = False 

639 

640 if not success: 

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

642 "for details.") 

643 

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

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

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

647 # only place to do so. 

648 self._cleanup(self.isMaster) 

649 

650 def forget_vdi(self, uuid): 

651 if not self.legacyMode: 

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

653 super(LVHDSR, self).forget_vdi(uuid) 

654 

655 def scan(self, uuid): 

656 activated_lvs = set() 

657 try: 

658 util.SMlog("LVHDSR.scan for %s" % self.uuid) 

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

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

661 raise xs_errors.XenError('LVMMaster') 

662 

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

664 self._expand_size() 

665 self.lvmCache.refresh() 

666 cbt_vdis = self.lvmCache.getTagged(CBTLOG_TAG) 

667 self._loadvdis() 

668 stats = lvutil._getVGstats(self.vgname) 

669 self.physical_size = stats['physical_size'] 

670 self.physical_utilisation = stats['physical_utilisation'] 

671 

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

673 # XAPI 

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

675 vdiToSnaps = {} 

676 # get VDIs from XAPI 

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

678 vdi_uuids = set([]) 

679 for vdi in vdis: 

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

681 

682 Dict = LVMMetadataHandler(self.mdpath, False).getMetadata()[1] 

683 

684 for vdi in list(Dict.keys()): 

685 vdi_uuid = Dict[vdi][UUID_TAG] 

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

687 if Dict[vdi][SNAPSHOT_OF_TAG] in vdiToSnaps: 

688 vdiToSnaps[Dict[vdi][SNAPSHOT_OF_TAG]].append(vdi_uuid) 

689 else: 

690 vdiToSnaps[Dict[vdi][SNAPSHOT_OF_TAG]] = [vdi_uuid] 

691 

692 if vdi_uuid not in vdi_uuids: 

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

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

695 sm_config = {} 

696 sm_config['vdi_type'] = Dict[vdi][VDI_TYPE_TAG] 

697 lvname = "%s%s" % \ 

698 (lvhdutil.LV_PREFIX[sm_config['vdi_type']], vdi_uuid) 

699 self.lvActivator.activate( 

700 vdi_uuid, lvname, LVActivator.NORMAL) 

701 activated_lvs.add(vdi_uuid) 

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

703 

704 if Dict[vdi][VDI_TYPE_TAG] == vhdutil.VDI_TYPE_RAW: 704 ↛ 705line 704 didn't jump to line 705, because the condition on line 704 was never true

705 size = self.lvmCache.getSize( \ 

706 lvhdutil.LV_PREFIX[vhdutil.VDI_TYPE_RAW] + \ 

707 vdi_uuid) 

708 utilisation = \ 

709 util.roundup(lvutil.LVM_SIZE_INCREMENT, 

710 int(size)) 

711 else: 

712 parent = \ 

713 vhdutil._getVHDParentNoCheck(lvPath) 

714 

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

716 sm_config['vhd-parent'] = parent[len( \ 

717 lvhdutil.LV_PREFIX[vhdutil.VDI_TYPE_VHD]):] 

718 size = vhdutil.getSizeVirt(lvPath) 

719 if self.provision == "thin": 719 ↛ 720line 719 didn't jump to line 720

720 utilisation = \ 

721 util.roundup(lvutil.LVM_SIZE_INCREMENT, 

722 vhdutil.calcOverheadEmpty(lvhdutil.MSIZE)) 

723 else: 

724 utilisation = lvhdutil.calcSizeVHDLV(int(size)) 

725 

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

727 vdi_uuid, 

728 Dict[vdi][NAME_LABEL_TAG], 

729 Dict[vdi][NAME_DESCRIPTION_TAG], 

730 self.sr_ref, 

731 Dict[vdi][TYPE_TAG], 

732 False, 

733 bool(int(Dict[vdi][READ_ONLY_TAG])), 

734 {}, 

735 vdi_uuid, 

736 {}, 

737 sm_config) 

738 

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

740 bool(int(Dict[vdi][MANAGED_TAG]))) 

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

742 str(size)) 

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

744 vdi_ref, str(utilisation)) 

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

746 vdi_ref, bool(int(Dict[vdi][IS_A_SNAPSHOT_TAG]))) 

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

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

749 vdi_ref, DateTime(Dict[vdi][SNAPSHOT_TIME_TAG])) 

750 if Dict[vdi][TYPE_TAG] == 'metadata': 750 ↛ 751line 750 didn't jump to line 751, because the condition on line 750 was never true

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

752 vdi_ref, Dict[vdi][METADATA_OF_POOL_TAG]) 

753 

754 # Update CBT status of disks either just added 

755 # or already in XAPI 

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

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

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

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

760 # For existing VDIs, update local state too 

761 # Scan in base class SR updates existing VDIs 

762 # again based on local states 

763 if vdi_uuid in self.vdis: 

764 self.vdis[vdi_uuid].cbt_enabled = True 

765 cbt_vdis.remove(cbt_logname) 

766 

767 # Now set the snapshot statuses correctly in XAPI 

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

769 try: 

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

771 except: 

772 # the source VDI no longer exists, continue 

773 continue 

774 

775 for snapvdi in vdiToSnaps[srcvdi]: 

776 try: 

777 # this might fail in cases where its already set 

778 snapref = \ 

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

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

781 except Exception as e: 

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

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

784 

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

786 # If we have items remaining in this list, 

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

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

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

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

791 new_vdi = self.vdi(cbt_uuid) 

792 new_vdi.ty = "cbt_metadata" 

793 new_vdi.cbt_enabled = True 

794 self.vdis[cbt_uuid] = new_vdi 

795 

796 super(LVHDSR, self).scan(uuid) 

797 self._kickGC() 

798 

799 finally: 

800 for vdi in activated_lvs: 

801 self.lvActivator.deactivate( 

802 vdi, LVActivator.NORMAL, False) 

803 

804 def update(self, uuid): 

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

806 return 

807 self._updateStats(uuid, 0) 

808 

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

810 return 

811 

812 # synch name_label in metadata with XAPI 

813 update_map = {} 

814 update_map = {METADATA_UPDATE_OBJECT_TYPE_TAG: \ 

815 METADATA_OBJECT_TYPE_SR, 

816 NAME_LABEL_TAG: util.to_plain_string( \ 

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

818 NAME_DESCRIPTION_TAG: util.to_plain_string( \ 

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

820 } 

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

822 

823 def _updateStats(self, uuid, virtAllocDelta): 

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

825 self.virtual_allocation = valloc + virtAllocDelta 

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

827 (uuid, self.virtual_allocation)) 

828 stats = lvutil._getVGstats(self.vgname) 

829 self.physical_size = stats['physical_size'] 

830 self.physical_utilisation = stats['physical_utilisation'] 

831 self._db_update() 

832 

833 @deviceCheck 

834 def probe(self): 

835 return lvutil.srlist_toxml( 

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

837 lvhdutil.VG_PREFIX, 

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

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

840 

841 def vdi(self, uuid): 

842 return LVHDVDI(self, uuid) 

843 

844 def _loadvdis(self): 

845 self.virtual_allocation = 0 

846 self.vdiInfo = lvhdutil.getVDIInfo(self.lvmCache) 

847 self.allVDIs = {} 

848 

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

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

851 continue 

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

853 raise xs_errors.XenError('VDIUnavailable', \ 

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

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

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

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

858 

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

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

861 if vdi.parent in self.vdis: 

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

863 if vdi.parent in geneology: 

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

865 else: 

866 geneology[vdi.parent] = [uuid] 

867 

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

869 # will be GC'ed 

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

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

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

873 del self.vdis[uuid] 

874 

875 def _ensureSpaceAvailable(self, amount_needed): 

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

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

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

879 (space_available, amount_needed)) 

880 raise xs_errors.XenError('SRNoSpace') 

881 

882 def _handleInterruptedCloneOps(self): 

883 entries = self.journaler.getAll(LVHDVDI.JRN_CLONE) 

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

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

886 self._handleInterruptedCloneOp(uuid, val) 

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

888 self.journaler.remove(LVHDVDI.JRN_CLONE, uuid) 

889 

890 def _handleInterruptedCoalesceLeaf(self): 

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

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

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

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

895 self.lvmCache.refresh() 

896 

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

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

899 operation. Rolling back is unsafe if the leaf VHDs have already been 

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

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

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

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

904 lvs = lvhdutil.getLVInfo(self.lvmCache) 

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

906 

907 # is there a "base copy" VDI? 

908 if not lvs.get(baseUuid): 

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

910 if lvs.get(origUuid): 

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

912 return 

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

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

915 

916 if forceUndo: 

917 util.SMlog("Explicit revert") 

918 self._undoCloneOp(lvs, origUuid, baseUuid, clonUuid) 

919 return 

920 

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

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

923 self._undoCloneOp(lvs, origUuid, baseUuid, clonUuid) 

924 return 

925 

926 vdis = lvhdutil.getVDIInfo(self.lvmCache) 

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

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

929 self._undoCloneOp(lvs, origUuid, baseUuid, clonUuid) 

930 return 

931 

932 orig = vdis[origUuid] 

933 base = vdis[baseUuid] 

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

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

936 if orig.parentUuid != baseUuid: 

937 parent = vdis[orig.parentUuid] 

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

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

940 if not vhdutil.check(origPath): 

941 util.SMlog("Orig VHD invalid => revert") 

942 self._undoCloneOp(lvs, origUuid, baseUuid, clonUuid) 

943 return 

944 

945 if clonUuid: 

946 clon = vdis[clonUuid] 

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

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

949 if not vhdutil.check(clonPath): 

950 util.SMlog("Clon VHD invalid => revert") 

951 self._undoCloneOp(lvs, origUuid, baseUuid, clonUuid) 

952 return 

953 

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

955 self._completeCloneOp(vdis, origUuid, baseUuid, clonUuid) 

956 

957 def _undoCloneOp(self, lvs, origUuid, baseUuid, clonUuid): 

958 base = lvs[baseUuid] 

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

960 

961 # make the parent RW 

962 if base.readonly: 

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

964 

965 ns = lvhdutil.NS_PREFIX_LVM + self.uuid 

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

967 origRefcountNormal = 0 

968 

969 # un-hide the parent 

970 vhdInfo = None 

971 if base.vdiType == vhdutil.VDI_TYPE_VHD: 

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

973 origRefcountNormal = 1 

974 vhdInfo = vhdutil.getVHDInfo(basePath, lvhdutil.extractUuid, False) 

975 if base.vdiType == vhdutil.VDI_TYPE_VHD and vhdInfo.hidden: 

976 vhdutil.setHidden(basePath, False) 

977 elif base.vdiType == vhdutil.VDI_TYPE_RAW and base.hidden: 

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

979 

980 # remove the child nodes 

981 if clonUuid and lvs.get(clonUuid): 

982 if lvs[clonUuid].vdiType != vhdutil.VDI_TYPE_VHD: 

983 raise util.SMException("clone %s not VHD" % clonUuid) 

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

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

986 self.lvActivator.remove(clonUuid, False) 

987 if lvs.get(origUuid): 

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

989 

990 # inflate the parent to fully-allocated size 

991 if base.vdiType == vhdutil.VDI_TYPE_VHD: 

992 fullSize = lvhdutil.calcSizeVHDLV(vhdInfo.sizeVirt) 

993 lvhdutil.inflate(self.journaler, self.uuid, baseUuid, fullSize) 

994 

995 # rename back 

996 origLV = lvhdutil.LV_PREFIX[base.vdiType] + origUuid 

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

998 RefCounter.reset(baseUuid, ns) 

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

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

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

1002 

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

1004 # flag to facilitate vm deactivate 

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

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

1007 

1008 # update LVM metadata on slaves 

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

1010 lvhdutil.lvRefreshOnSlaves(self.session, self.uuid, self.vgname, 

1011 origLV, origUuid, slaves) 

1012 

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

1014 

1015 def _completeCloneOp(self, vdis, origUuid, baseUuid, clonUuid): 

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

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

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

1019 would cause a deadlock)""" 

1020 base = vdis[baseUuid] 

1021 clon = None 

1022 if clonUuid: 

1023 clon = vdis[clonUuid] 

1024 

1025 cleanup.abort(self.uuid) 

1026 

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

1028 if not base.hidden: 

1029 if base.vdiType == vhdutil.VDI_TYPE_RAW: 

1030 self.lvmCache.setHidden(base.lvName) 

1031 else: 

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

1033 vhdutil.setHidden(basePath) 

1034 if not base.lvReadonly: 

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

1036 

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

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

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

1040 

1041 # Update the original record 

1042 try: 

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

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

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

1046 sm_config["vdi_type"] = vhdutil.VDI_TYPE_VHD 

1047 sm_config['vhd-parent'] = baseUuid 

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

1049 except XenAPI.Failure: 

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

1051 

1052 # introduce the new VDI records 

1053 if clonUuid: 

1054 try: 

1055 clon_vdi = VDI.VDI(self, clonUuid) 

1056 clon_vdi.read_only = False 

1057 clon_vdi.location = clonUuid 

1058 clon_vdi.utilisation = clon.sizeLV 

1059 clon_vdi.sm_config = { 

1060 "vdi_type": vhdutil.VDI_TYPE_VHD, 

1061 "vhd-parent": baseUuid} 

1062 

1063 if not self.legacyMode: 

1064 LVMMetadataHandler(self.mdpath). \ 

1065 ensureSpaceIsAvailableForVdis(1) 

1066 

1067 clon_vdi_ref = clon_vdi._db_introduce() 

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

1069 (clon_vdi_ref, clonUuid)) 

1070 

1071 vdi_info = {UUID_TAG: clonUuid, 

1072 NAME_LABEL_TAG: clon_vdi.label, 

1073 NAME_DESCRIPTION_TAG: clon_vdi.description, 

1074 IS_A_SNAPSHOT_TAG: 0, 

1075 SNAPSHOT_OF_TAG: '', 

1076 SNAPSHOT_TIME_TAG: '', 

1077 TYPE_TAG: type, 

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

1079 READ_ONLY_TAG: int(clon_vdi.read_only), 

1080 MANAGED_TAG: int(clon_vdi.managed), 

1081 METADATA_OF_POOL_TAG: '' 

1082 } 

1083 

1084 if not self.legacyMode: 

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

1086 

1087 except XenAPI.Failure: 

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

1089 

1090 try: 

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

1092 base_vdi.label = "base copy" 

1093 base_vdi.read_only = True 

1094 base_vdi.location = baseUuid 

1095 base_vdi.size = base.sizeVirt 

1096 base_vdi.utilisation = base.sizeLV 

1097 base_vdi.managed = False 

1098 base_vdi.sm_config = { 

1099 "vdi_type": vhdutil.VDI_TYPE_VHD, 

1100 "vhd-parent": baseUuid} 

1101 

1102 if not self.legacyMode: 

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

1104 

1105 base_vdi_ref = base_vdi._db_introduce() 

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

1107 (base_vdi_ref, baseUuid)) 

1108 

1109 vdi_info = {UUID_TAG: baseUuid, 

1110 NAME_LABEL_TAG: base_vdi.label, 

1111 NAME_DESCRIPTION_TAG: base_vdi.description, 

1112 IS_A_SNAPSHOT_TAG: 0, 

1113 SNAPSHOT_OF_TAG: '', 

1114 SNAPSHOT_TIME_TAG: '', 

1115 TYPE_TAG: type, 

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

1117 READ_ONLY_TAG: int(base_vdi.read_only), 

1118 MANAGED_TAG: int(base_vdi.managed), 

1119 METADATA_OF_POOL_TAG: '' 

1120 } 

1121 

1122 if not self.legacyMode: 

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

1124 except XenAPI.Failure: 

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

1126 

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

1128 

1129 def _undoAllJournals(self): 

1130 """Undo all VHD & SM interrupted journaled operations. This call must 

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

1132 # undoing interrupted inflates must be done first, since undoing VHD 

1133 # ops might require inflations 

1134 self.lock.acquire() 

1135 try: 

1136 self._undoAllInflateJournals() 

1137 self._undoAllVHDJournals() 

1138 self._handleInterruptedCloneOps() 

1139 self._handleInterruptedCoalesceLeaf() 

1140 finally: 

1141 self.lock.release() 

1142 self.cleanup() 

1143 

1144 def _undoAllInflateJournals(self): 

1145 entries = self.journaler.getAll(lvhdutil.JRN_INFLATE) 

1146 if len(entries) == 0: 

1147 return 

1148 self._loadvdis() 

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

1150 vdi = self.vdis.get(uuid) 

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

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

1153 (uuid, vdi.path, val)) 

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

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

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

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

1158 util.zeroOut(vdi.path, currSizeLV - vhdutil.VHD_FOOTER_SIZE, 

1159 vhdutil.VHD_FOOTER_SIZE) 

1160 lvhdutil.deflate(self.lvmCache, vdi.lvname, int(val)) 

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

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

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

1164 lvhdutil.lvRefreshOnAllSlaves(self.session, self.uuid, 

1165 self.vgname, vdi.lvname, uuid) 

1166 self.journaler.remove(lvhdutil.JRN_INFLATE, uuid) 

1167 delattr(self, "vdiInfo") 

1168 delattr(self, "allVDIs") 

1169 

1170 def _undoAllVHDJournals(self): 

1171 """check if there are VHD journals in existence and revert them""" 

1172 journals = lvhdutil.getAllVHDJournals(self.lvmCache) 

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

1174 return 

1175 self._loadvdis() 

1176 for uuid, jlvName in journals: 

1177 vdi = self.vdis[uuid] 

1178 util.SMlog("Found VHD journal %s, reverting %s" % (uuid, vdi.path)) 

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

1180 self.lvmCache.activateNoRefcount(jlvName) 

1181 fullSize = lvhdutil.calcSizeVHDLV(vdi.size) 

1182 lvhdutil.inflate(self.journaler, self.uuid, vdi.uuid, fullSize) 

1183 try: 

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

1185 vhdutil.revert(vdi.path, jFile) 

1186 except util.CommandException: 

1187 util.logException("VHD journal revert") 

1188 vhdutil.check(vdi.path) 

1189 util.SMlog("VHD revert failed but VHD ok: removing journal") 

1190 # Attempt to reclaim unused space 

1191 vhdInfo = vhdutil.getVHDInfo(vdi.path, lvhdutil.extractUuid, False) 

1192 NewSize = lvhdutil.calcSizeVHDLV(vhdInfo.sizeVirt) 

1193 if NewSize < fullSize: 

1194 lvhdutil.deflate(self.lvmCache, vdi.lvname, int(NewSize)) 

1195 lvhdutil.lvRefreshOnAllSlaves(self.session, self.uuid, 

1196 self.vgname, vdi.lvname, uuid) 

1197 self.lvmCache.remove(jlvName) 

1198 delattr(self, "vdiInfo") 

1199 delattr(self, "allVDIs") 

1200 

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

1202 master_ref = util.get_this_host_ref(self.session) 

1203 for hostRef in host_refs: 

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

1205 continue 

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

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

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

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

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

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

1212 

1213 def _updateSlavesPreClone(self, hostRefs, origOldLV): 

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

1215 "action1": "deactivateNoRefcount", 

1216 "lvName1": origOldLV} 

1217 message = "Deactivate VDI" 

1218 self.call_on_slave(args, hostRefs, message) 

1219 

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

1221 baseUuid, baseLV): 

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

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

1224 for the base LV""" 

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

1226 "action1": "refresh", 

1227 "lvName1": origLV, 

1228 "action2": "activate", 

1229 "ns2": lvhdutil.NS_PREFIX_LVM + self.uuid, 

1230 "lvName2": baseLV, 

1231 "uuid2": baseUuid} 

1232 

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

1234 self.call_on_slave(args, hostRefs, message) 

1235 

1236 def _updateSlavesOnCBTClone(self, hostRefs, cbtlog): 

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

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

1239 "action1": "deactivateNoRefcount", 

1240 "lvName1": cbtlog, 

1241 "action2": "refresh", 

1242 "lvName2": cbtlog} 

1243 

1244 message = f"Updating {cbtlog}" 

1245 self.call_on_slave(args, hostRefs, message) 

1246 

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

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

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

1250 "action1": "cleanupLockAndRefcount", 

1251 "uuid1": baseUuid, 

1252 "ns1": lvhdutil.NS_PREFIX_LVM + self.uuid} 

1253 

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

1255 self.call_on_slave(args, hostRefs, message) 

1256 

1257 def _cleanup(self, skipLockCleanup=False): 

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

1259 RefCounter.resetAll(lvhdutil.NS_PREFIX_LVM + self.uuid) 

1260 IPCFlag(self.uuid).clearAll() 

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

1262 Lock.cleanupAll(self.uuid) 

1263 Lock.cleanupAll(lvhdutil.NS_PREFIX_LVM + self.uuid) 

1264 

1265 def _prepareTestMode(self): 

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

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

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

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

1270 

1271 def _kickGC(self): 

1272 util.SMlog("Kicking GC") 

1273 cleanup.start_gc_service(self.uuid) 

1274 

1275 def ensureCBTSpace(self): 

1276 # Ensure we have space for at least one LV 

1277 self._ensureSpaceAvailable(self.journaler.LV_SIZE) 

1278 

1279 

1280class LVHDVDI(VDI.VDI): 

1281 

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

1283 

1284 def load(self, vdi_uuid): 

1285 self.lock = self.sr.lock 

1286 self.lvActivator = self.sr.lvActivator 

1287 self.loaded = False 

1288 self.vdi_type = vhdutil.VDI_TYPE_VHD 

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

1290 self.vdi_type = vhdutil.VDI_TYPE_RAW 

1291 self.uuid = vdi_uuid 

1292 self.location = self.uuid 

1293 self.exists = True 

1294 

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

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

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

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

1299 else: 

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

1301 return 

1302 

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

1304 if self._determineType(): 

1305 return 

1306 

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

1308 self.exists = False 

1309 if "vdi_sm_config" in self.sr.srcmd.params and \ 1309 ↛ 1311line 1309 didn't jump to line 1311, because the condition on line 1309 was never true

1310 "type" in self.sr.srcmd.params["vdi_sm_config"]: 

1311 type = self.sr.srcmd.params["vdi_sm_config"]["type"] 

1312 if type == PARAM_RAW: 

1313 self.vdi_type = vhdutil.VDI_TYPE_RAW 

1314 elif type == PARAM_VHD: 

1315 self.vdi_type = vhdutil.VDI_TYPE_VHD 

1316 if self.sr.cmd == 'vdi_create' and self.sr.legacyMode: 

1317 raise xs_errors.XenError('VDICreate', \ 

1318 opterr='Cannot create VHD type disk in legacy mode') 

1319 else: 

1320 raise xs_errors.XenError('VDICreate', opterr='bad type') 

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

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

1323 

1324 def create(self, sr_uuid, vdi_uuid, size): 

1325 util.SMlog("LVHDVDI.create for %s" % self.uuid) 

1326 if not self.sr.isMaster: 

1327 raise xs_errors.XenError('LVMMaster') 

1328 if self.exists: 

1329 raise xs_errors.XenError('VDIExists') 

1330 

1331 size = vhdutil.validate_and_round_vhd_size(int(size)) 

1332 

1333 util.SMlog("LVHDVDI.create: type = %s, %s (size=%s)" % \ 

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

1335 lvSize = 0 

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

1337 if self.vdi_type == vhdutil.VDI_TYPE_RAW: 

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

1339 else: 

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

1341 lvSize = util.roundup(lvutil.LVM_SIZE_INCREMENT, 

1342 vhdutil.calcOverheadEmpty(lvhdutil.MSIZE)) 

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

1344 lvSize = lvhdutil.calcSizeVHDLV(int(size)) 

1345 

1346 self.sr._ensureSpaceAvailable(lvSize) 

1347 

1348 try: 

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

1350 if self.vdi_type == vhdutil.VDI_TYPE_RAW: 

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

1352 else: 

1353 vhdutil.create(self.path, int(size), False, lvhdutil.MSIZE_MB) 

1354 self.size = vhdutil.getSizeVirt(self.path) 

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

1356 except util.CommandException as e: 

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

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

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

1360 

1361 self.utilisation = lvSize 

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

1363 

1364 if not self.sr.legacyMode: 

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

1366 

1367 self.ref = self._db_introduce() 

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

1369 

1370 vdi_info = {UUID_TAG: self.uuid, 

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

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

1373 IS_A_SNAPSHOT_TAG: 0, 

1374 SNAPSHOT_OF_TAG: '', 

1375 SNAPSHOT_TIME_TAG: '', 

1376 TYPE_TAG: self.ty, 

1377 VDI_TYPE_TAG: self.vdi_type, 

1378 READ_ONLY_TAG: int(self.read_only), 

1379 MANAGED_TAG: int(self.managed), 

1380 METADATA_OF_POOL_TAG: '' 

1381 } 

1382 

1383 if not self.sr.legacyMode: 

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

1385 

1386 return VDI.VDI.get_params(self) 

1387 

1388 def delete(self, sr_uuid, vdi_uuid, data_only=False): 

1389 util.SMlog("LVHDVDI.delete for %s" % self.uuid) 

1390 try: 

1391 self._loadThis() 

1392 except xs_errors.SRException as e: 

1393 # Catch 'VDI doesn't exist' exception 

1394 if e.errno == 46: 

1395 return super(LVHDVDI, self).delete(sr_uuid, vdi_uuid, data_only) 

1396 raise 

1397 

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

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

1400 raise xs_errors.XenError("VDIDelete", \ 

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

1402 

1403 if not self.hidden: 

1404 self._markHidden() 

1405 

1406 if not data_only: 

1407 # Remove from XAPI and delete from MGT 

1408 self._db_forget() 

1409 else: 

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

1411 # Only delete from MGT 

1412 if not self.sr.legacyMode: 

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

1414 

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

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

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

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

1419 

1420 try: 

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

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

1423 self.sr.lock.cleanupAll(vdi_uuid) 

1424 except xs_errors.SRException as e: 

1425 util.SMlog( 

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

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

1428 

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

1430 self.sr._kickGC() 

1431 return super(LVHDVDI, self).delete(sr_uuid, vdi_uuid, data_only) 

1432 

1433 def attach(self, sr_uuid, vdi_uuid): 

1434 util.SMlog("LVHDVDI.attach for %s" % self.uuid) 

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

1436 raise xs_errors.XenError('VDIUnavailable', 

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

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

1439 

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

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

1442 needInflate = True 

1443 if self.vdi_type == vhdutil.VDI_TYPE_RAW or not writable: 

1444 needInflate = False 

1445 else: 

1446 self._loadThis() 

1447 if self.utilisation >= lvhdutil.calcSizeVHDLV(self.size): 

1448 needInflate = False 

1449 

1450 if needInflate: 

1451 try: 

1452 self._prepareThin(True) 

1453 except: 

1454 util.logException("attach") 

1455 raise xs_errors.XenError('LVMProvisionAttach') 

1456 

1457 try: 

1458 return self._attach() 

1459 finally: 

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

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

1462 

1463 def detach(self, sr_uuid, vdi_uuid): 

1464 util.SMlog("LVHDVDI.detach for %s" % self.uuid) 

1465 self._loadThis() 

1466 already_deflated = (self.utilisation < \ 

1467 lvhdutil.calcSizeVHDLV(self.size)) 

1468 needDeflate = True 

1469 if self.vdi_type == vhdutil.VDI_TYPE_RAW or already_deflated: 

1470 needDeflate = False 

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

1472 needDeflate = False 

1473 # except for snapshots, which are always deflated 

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

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

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

1477 if snap: 

1478 needDeflate = True 

1479 

1480 if needDeflate: 

1481 try: 

1482 self._prepareThin(False) 

1483 except: 

1484 util.logException("_prepareThin") 

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

1486 

1487 try: 

1488 self._detach() 

1489 finally: 

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

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

1492 

1493 # We only support offline resize 

1494 def resize(self, sr_uuid, vdi_uuid, size): 

1495 util.SMlog("LVHDVDI.resize for %s" % self.uuid) 

1496 if not self.sr.isMaster: 

1497 raise xs_errors.XenError('LVMMaster') 

1498 

1499 self._loadThis() 

1500 if self.hidden: 

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

1502 

1503 if size < self.size: 

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

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

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

1507 

1508 size = vhdutil.validate_and_round_vhd_size(int(size)) 

1509 

1510 if size == self.size: 

1511 return VDI.VDI.get_params(self) 

1512 

1513 if self.vdi_type == vhdutil.VDI_TYPE_RAW: 

1514 lvSizeOld = self.size 

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

1516 else: 

1517 lvSizeOld = self.utilisation 

1518 lvSizeNew = lvhdutil.calcSizeVHDLV(size) 

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

1520 # VDI is currently deflated, so keep it deflated 

1521 lvSizeNew = lvSizeOld 

1522 assert(lvSizeNew >= lvSizeOld) 

1523 spaceNeeded = lvSizeNew - lvSizeOld 

1524 self.sr._ensureSpaceAvailable(spaceNeeded) 

1525 

1526 oldSize = self.size 

1527 if self.vdi_type == vhdutil.VDI_TYPE_RAW: 

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

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

1530 self.utilisation = self.size 

1531 else: 

1532 if lvSizeNew != lvSizeOld: 

1533 lvhdutil.inflate(self.sr.journaler, self.sr.uuid, self.uuid, 

1534 lvSizeNew) 

1535 vhdutil.setSizeVirtFast(self.path, size) 

1536 self.size = vhdutil.getSizeVirt(self.path) 

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

1538 

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

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

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

1542 str(self.utilisation)) 

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

1544 super(LVHDVDI, self).resize_cbt(self.sr.uuid, self.uuid, self.size) 

1545 return VDI.VDI.get_params(self) 

1546 

1547 def clone(self, sr_uuid, vdi_uuid): 

1548 return self._do_snapshot( 

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

1550 

1551 def compose(self, sr_uuid, vdi1, vdi2): 

1552 util.SMlog("LVHDSR.compose for %s -> %s" % (vdi2, vdi1)) 

1553 if self.vdi_type != vhdutil.VDI_TYPE_VHD: 

1554 raise xs_errors.XenError('Unimplemented') 

1555 

1556 parent_uuid = vdi1 

1557 parent_lvname = lvhdutil.LV_PREFIX[vhdutil.VDI_TYPE_VHD] + parent_uuid 

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

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

1560 

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

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

1563 

1564 vhdutil.setParent(self.path, parent_path, False) 

1565 vhdutil.setHidden(parent_path) 

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

1567 

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

1569 True): 

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

1571 

1572 util.SMlog("Compose done") 

1573 

1574 def reset_leaf(self, sr_uuid, vdi_uuid): 

1575 util.SMlog("LVHDSR.reset_leaf for %s" % vdi_uuid) 

1576 if self.vdi_type != vhdutil.VDI_TYPE_VHD: 

1577 raise xs_errors.XenError('Unimplemented') 

1578 

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

1580 

1581 # safety check 

1582 if not vhdutil.hasParent(self.path): 

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

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

1585 

1586 vhdutil.killData(self.path) 

1587 

1588 def _attach(self): 

1589 self._chainSetActive(True, True, True) 

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

1591 raise xs_errors.XenError('VDIUnavailable', \ 

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

1593 

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

1595 self.xenstore_data = {} 

1596 

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

1598 scsiutil.gen_synthetic_page_data(self.uuid))) 

1599 

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

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

1602 

1603 self.attached = True 

1604 self.sr.lvActivator.persist() 

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

1606 

1607 def _detach(self): 

1608 self._chainSetActive(False, True) 

1609 self.attached = False 

1610 

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

1612 cloneOp=False, secondary=None, cbtlog=None): 

1613 # If cbt enabled, save file consistency state 

1614 if cbtlog is not None: 

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

1616 consistency_state = False 

1617 else: 

1618 consistency_state = True 

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

1620 (consistency_state, vdi_uuid)) 

1621 else: 

1622 consistency_state = None 

1623 

1624 pause_time = time.time() 

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

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

1627 

1628 snapResult = None 

1629 try: 

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

1631 except Exception as e1: 

1632 try: 

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

1634 secondary=None) 

1635 except Exception as e2: 

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

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

1638 raise 

1639 self.disable_leaf_on_secondary(vdi_uuid, secondary=secondary) 

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

1641 unpause_time = time.time() 

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

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

1644 (unpause_time - pause_time)) 

1645 return snapResult 

1646 

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

1648 util.SMlog("LVHDVDI._snapshot for %s (type %s)" % (self.uuid, snapType)) 

1649 

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

1651 raise xs_errors.XenError('LVMMaster') 

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

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

1654 

1655 self._loadThis() 

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

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

1658 

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

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

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

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

1663 raise xs_errors.XenError('Unimplemented', \ 

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

1665 

1666 # we must activate the entire VHD chain because the real parent could 

1667 # theoretically be anywhere in the chain if all VHDs under it are empty 

1668 self._chainSetActive(True, False) 

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

1670 raise xs_errors.XenError('VDIUnavailable', \ 

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

1672 

1673 if self.vdi_type == vhdutil.VDI_TYPE_VHD: 1673 ↛ 1681line 1673 didn't jump to line 1681, because the condition on line 1673 was never false

1674 depth = vhdutil.getDepth(self.path) 

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

1676 raise xs_errors.XenError('VDIUnavailable', \ 

1677 opterr='failed to get VHD depth') 

1678 elif depth >= vhdutil.MAX_CHAIN_SIZE: 1678 ↛ 1679line 1678 didn't jump to line 1679, because the condition on line 1678 was never true

1679 raise xs_errors.XenError('SnapshotChainTooLong') 

1680 

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

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

1683 

1684 fullpr = lvhdutil.calcSizeVHDLV(self.size) 

1685 thinpr = util.roundup(lvutil.LVM_SIZE_INCREMENT, \ 

1686 vhdutil.calcOverheadEmpty(lvhdutil.MSIZE)) 

1687 lvSizeOrig = thinpr 

1688 lvSizeClon = thinpr 

1689 

1690 hostRefs = [] 

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

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

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

1694 lvSizeOrig = fullpr 

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

1696 if not self.issnap: 

1697 lvSizeOrig = fullpr 

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

1699 lvSizeClon = fullpr 

1700 

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

1702 snapType == VDI.SNAPSHOT_INTERNAL): 

1703 lvSizeClon = 0 

1704 

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

1706 # inflate journal (for the failure handling 

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

1708 lvSizeBase = self.size 

1709 if self.vdi_type == vhdutil.VDI_TYPE_VHD: 1709 ↛ 1713line 1709 didn't jump to line 1713, because the condition on line 1709 was never false

1710 lvSizeBase = util.roundup(lvutil.LVM_SIZE_INCREMENT, 

1711 vhdutil.getSizePhys(self.path)) 

1712 size_req -= (self.utilisation - lvSizeBase) 

1713 self.sr._ensureSpaceAvailable(size_req) 

1714 

1715 if hostRefs: 

1716 self.sr._updateSlavesPreClone(hostRefs, self.lvname) 

1717 

1718 baseUuid = util.gen_uuid() 

1719 origUuid = self.uuid 

1720 clonUuid = "" 

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

1722 clonUuid = util.gen_uuid() 

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

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

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

1726 

1727 try: 

1728 # self becomes the "base vdi" 

1729 origOldLV = self.lvname 

1730 baseLV = lvhdutil.LV_PREFIX[self.vdi_type] + baseUuid 

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

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

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

1734 self.uuid = baseUuid 

1735 self.lvname = baseLV 

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

1737 self.label = "base copy" 

1738 self.read_only = True 

1739 self.location = self.uuid 

1740 self.managed = False 

1741 

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

1743 # the snapshot volumes to avoid requiring double the space 

1744 if self.vdi_type == vhdutil.VDI_TYPE_VHD: 1744 ↛ 1747line 1744 didn't jump to line 1747, because the condition on line 1744 was never false

1745 lvhdutil.deflate(self.sr.lvmCache, self.lvname, lvSizeBase) 

1746 self.utilisation = lvSizeBase 

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

1748 

1749 snapVDI = self._createSnap(origUuid, lvSizeOrig, False) 

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

1751 snapVDI2 = None 

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

1753 snapVDI2 = self._createSnap(clonUuid, lvSizeClon, True) 

1754 # If we have CBT enabled on the VDI, 

1755 # set CBT status for the new snapshot disk 

1756 if cbtlog: 

1757 snapVDI2.cbt_enabled = True 

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

1759 

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

1761 # new VHD children have been created, which are referencing it; 

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

1763 # the parent before we snapshot it 

1764 if self.vdi_type == vhdutil.VDI_TYPE_RAW: 1764 ↛ 1765line 1764 didn't jump to line 1765, because the condition on line 1764 was never true

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

1766 else: 

1767 vhdutil.setHidden(self.path) 

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

1769 

1770 # set the base copy to ReadOnly 

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

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

1773 

1774 if hostRefs: 

1775 self.sr._updateSlavesOnClone(hostRefs, origOldLV, 

1776 snapVDI.lvname, self.uuid, self.lvname) 

1777 

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

1779 if snapType == VDI.SNAPSHOT_DOUBLE and cbtlog: 

1780 snapVDI._cbt_snapshot(clonUuid, cbt_consistency) 

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

1782 cbtlog_file = self._get_cbt_logname(snapVDI.uuid) 

1783 try: 

1784 self.sr._updateSlavesOnCBTClone(hostRefs, cbtlog_file) 

1785 except: 

1786 alert_name = "VDI_CBT_SNAPSHOT_FAILED" 

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

1788 .format(snapVDI.uuid)) 

1789 snapVDI._disable_cbt_on_error(alert_name, alert_str) 

1790 pass 

1791 

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

1793 util.logException("LVHDVDI._snapshot") 

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

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

1796 

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

1798 

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

1800 

1801 def _createSnap(self, snapUuid, snapSizeLV, isNew): 

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

1803 snapLV = lvhdutil.LV_PREFIX[vhdutil.VDI_TYPE_VHD] + snapUuid 

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

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

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

1807 if isNew: 

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

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

1810 parentRaw = (self.vdi_type == vhdutil.VDI_TYPE_RAW) 

1811 vhdutil.snapshot(snapPath, self.path, parentRaw, lvhdutil.MSIZE_MB) 

1812 snapParent = vhdutil.getParent(snapPath, lvhdutil.extractUuid) 

1813 

1814 snapVDI = LVHDVDI(self.sr, snapUuid) 

1815 snapVDI.read_only = False 

1816 snapVDI.location = snapUuid 

1817 snapVDI.size = self.size 

1818 snapVDI.utilisation = snapSizeLV 

1819 snapVDI.sm_config = dict() 

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

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

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

1823 not key.startswith("host_"): 

1824 snapVDI.sm_config[key] = val 

1825 snapVDI.sm_config["vdi_type"] = vhdutil.VDI_TYPE_VHD 

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

1827 snapVDI.lvname = snapLV 

1828 return snapVDI 

1829 

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

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

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

1833 basePresent = True 

1834 

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

1836 # unused 

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

1838 snap2Parent = "" 

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

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

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

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

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

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

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

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

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

1848 if hostRefs: 

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

1850 basePresent = False 

1851 else: 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1868 ns = lvhdutil.NS_PREFIX_LVM + self.sr.uuid 

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

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

1871 

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

1873 # the leaf without being inherited by anyone else 

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

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

1876 del self.sm_config[key] 

1877 

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

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

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

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

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

1883 vdiRef = snapVDI2._db_introduce() 

1884 if cloneOp: 

1885 vdi_info = {UUID_TAG: snapVDI2.uuid, 

1886 NAME_LABEL_TAG: util.to_plain_string( \ 

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

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

1889 NAME_DESCRIPTION_TAG: util.to_plain_string( \ 

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

1891 IS_A_SNAPSHOT_TAG: 0, 

1892 SNAPSHOT_OF_TAG: '', 

1893 SNAPSHOT_TIME_TAG: '', 

1894 TYPE_TAG: type, 

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

1896 READ_ONLY_TAG: 0, 

1897 MANAGED_TAG: int(snapVDI2.managed), 

1898 METADATA_OF_POOL_TAG: '' 

1899 } 

1900 else: 

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

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

1903 vdi_info = {UUID_TAG: snapVDI2.uuid, 

1904 NAME_LABEL_TAG: util.to_plain_string( \ 

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

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

1907 NAME_DESCRIPTION_TAG: util.to_plain_string( \ 

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

1909 IS_A_SNAPSHOT_TAG: 1, 

1910 SNAPSHOT_OF_TAG: snapVDI.uuid, 

1911 SNAPSHOT_TIME_TAG: '', 

1912 TYPE_TAG: type, 

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

1914 READ_ONLY_TAG: 0, 

1915 MANAGED_TAG: int(snapVDI2.managed), 

1916 METADATA_OF_POOL_TAG: '' 

1917 } 

1918 

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

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

1921 (vdiRef, snapVDI2.uuid)) 

1922 

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

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

1925 vdiRef = self._db_introduce() 

1926 vdi_info = {UUID_TAG: self.uuid, 

1927 NAME_LABEL_TAG: self.label, 

1928 NAME_DESCRIPTION_TAG: self.description, 

1929 IS_A_SNAPSHOT_TAG: 0, 

1930 SNAPSHOT_OF_TAG: '', 

1931 SNAPSHOT_TIME_TAG: '', 

1932 TYPE_TAG: type, 

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

1934 READ_ONLY_TAG: 1, 

1935 MANAGED_TAG: 0, 

1936 METADATA_OF_POOL_TAG: '' 

1937 } 

1938 

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

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

1941 (vdiRef, self.uuid)) 

1942 

1943 # Update the original record 

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

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

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

1947 str(snapVDI.utilisation)) 

1948 

1949 # Return the info on the new snap VDI 

1950 snap = snapVDI2 

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

1952 snap = self 

1953 if not basePresent: 

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

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

1956 # normally try to single-snapshot an empty VHD of course, but 

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

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

1959 snap = snapVDI 

1960 return snap.get_params() 

1961 

1962 def _initFromVDIInfo(self, vdiInfo): 

1963 self.vdi_type = vdiInfo.vdiType 

1964 self.lvname = vdiInfo.lvName 

1965 self.size = vdiInfo.sizeVirt 

1966 self.utilisation = vdiInfo.sizeLV 

1967 self.hidden = vdiInfo.hidden 

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

1969 self.managed = False 

1970 self.active = vdiInfo.lvActive 

1971 self.readonly = vdiInfo.lvReadonly 

1972 self.parent = vdiInfo.parentUuid 

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

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

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

1976 else: 

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

1978 self.loaded = True 

1979 

1980 def _initFromLVInfo(self, lvInfo): 

1981 self.vdi_type = lvInfo.vdiType 

1982 self.lvname = lvInfo.name 

1983 self.size = lvInfo.size 

1984 self.utilisation = lvInfo.size 

1985 self.hidden = lvInfo.hidden 

1986 self.active = lvInfo.active 

1987 self.readonly = lvInfo.readonly 

1988 self.parent = '' 

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

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

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

1992 else: 

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

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

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

1996 if self.vdi_type == vhdutil.VDI_TYPE_RAW: 1996 ↛ 1997line 1996 didn't jump to line 1997, because the condition on line 1996 was never true

1997 self.loaded = True 

1998 

1999 def _initFromVHDInfo(self, vhdInfo): 

2000 self.size = vhdInfo.sizeVirt 

2001 if (self.parent == '' or (vhdInfo.parentUuid != '' 2001 ↛ 2004line 2001 didn't jump to line 2004, because the condition on line 2001 was never false

2002 and vhdInfo.parentUuid != self.parent)): 

2003 self.parent = vhdInfo.parentUuid 

2004 self.hidden = vhdInfo.hidden 

2005 self.loaded = True 

2006 

2007 def _determineType(self): 

2008 """Determine whether this is a raw or a VHD VDI""" 

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

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

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

2012 if sm_config.get("vdi_type"): 

2013 self.vdi_type = sm_config["vdi_type"] 

2014 prefix = lvhdutil.LV_PREFIX[self.vdi_type] 

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

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

2017 self.sm_config_override = sm_config 

2018 return True 

2019 

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

2021 # the LV is active 

2022 found = False 

2023 for t in lvhdutil.VDI_TYPES: 2023 ↛ 2024line 2023 didn't jump to line 2024, because the loop on line 2023 never started

2024 lvname = "%s%s" % (lvhdutil.LV_PREFIX[t], self.uuid) 

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

2026 if util.pathexists(path): 

2027 if found: 

2028 raise xs_errors.XenError('VDILoad', 

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

2030 found = True 

2031 self.vdi_type = t 

2032 self.lvname = lvname 

2033 self.path = path 

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

2035 return True 

2036 

2037 # now list all LV's 

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

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

2040 return False 

2041 

2042 lvs = lvhdutil.getLVInfo(self.sr.lvmCache) 

2043 if lvs.get(self.uuid): 

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

2045 return True 

2046 return False 

2047 

2048 def _loadThis(self): 

2049 """Load VDI info for this VDI and activate the LV if it's VHD. We 

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

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

2052 if self.vdi_type == vhdutil.VDI_TYPE_VHD: 

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

2054 return 

2055 try: 

2056 lvs = lvhdutil.getLVInfo(self.sr.lvmCache, self.lvname) 

2057 except util.CommandException as e: 

2058 raise xs_errors.XenError('VDIUnavailable', 

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

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

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

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

2063 if self.vdi_type == vhdutil.VDI_TYPE_VHD: 2063 ↛ 2070line 2063 didn't jump to line 2070, because the condition on line 2063 was never false

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

2065 vhdInfo = vhdutil.getVHDInfo(self.path, lvhdutil.extractUuid, False) 

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

2067 raise xs_errors.XenError('VDIUnavailable', \ 

2068 opterr='getVHDInfo failed') 

2069 self._initFromVHDInfo(vhdInfo) 

2070 self.loaded = True 

2071 

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

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

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

2075 lvhdutil.NS_PREFIX_LVM + self.sr.uuid) 

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

2077 return # this is a redundant activation/deactivation call 

2078 

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

2080 if self.vdi_type == vhdutil.VDI_TYPE_VHD: 2080 ↛ 2083line 2080 didn't jump to line 2083, because the condition on line 2080 was never false

2081 vdiList = vhdutil.getParentChain(self.lvname, 

2082 lvhdutil.extractUuid, self.sr.vgname) 

2083 for uuid, lvName in vdiList.items(): 2083 ↛ 2084line 2083 didn't jump to line 2084, because the loop on line 2083 never started

2084 binaryParam = binary 

2085 if uuid != self.uuid: 

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

2087 if active: 

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

2089 persistent) 

2090 else: 

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

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

2093 # operation 

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

2095 

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

2097 try: 

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

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

2100 except Exception as e: 

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

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

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

2104 

2105 def _markHidden(self): 

2106 if self.vdi_type == vhdutil.VDI_TYPE_RAW: 

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

2108 else: 

2109 vhdutil.setHidden(self.path) 

2110 self.hidden = 1 

2111 

2112 def _prepareThin(self, attach): 

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

2114 if self.sr.isMaster: 

2115 # the master can prepare the VDI locally 

2116 if attach: 

2117 lvhdutil.attachThin(self.sr.journaler, self.sr.uuid, self.uuid) 

2118 else: 

2119 lvhdutil.detachThin(self.session, self.sr.lvmCache, 

2120 self.sr.uuid, self.uuid) 

2121 else: 

2122 fn = "attach" 

2123 if not attach: 

2124 fn = "detach" 

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

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

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

2128 master, self.sr.THIN_PLUGIN, fn, 

2129 {"srUuid": self.sr.uuid, "vdiUuid": self.uuid}) 

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

2131 if not rv: 

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

2133 # refresh to pick up the size change on this slave 

2134 self.sr.lvmCache.activateNoRefcount(self.lvname, True) 

2135 

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

2137 if origUtilisation != self.utilisation: 

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

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

2140 str(self.utilisation)) 

2141 stats = lvutil._getVGstats(self.sr.vgname) 

2142 sr_utilisation = stats['physical_utilisation'] 

2143 self.session.xenapi.SR.set_physical_utilisation(self.sr.sr_ref, 

2144 str(sr_utilisation)) 

2145 

2146 def update(self, sr_uuid, vdi_uuid): 

2147 if self.sr.legacyMode: 

2148 return 

2149 

2150 #Synch the name_label of this VDI on storage with the name_label in XAPI 

2151 vdi_ref = self.session.xenapi.VDI.get_by_uuid(self.uuid) 

2152 update_map = {} 

2153 update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] = \ 

2154 METADATA_OBJECT_TYPE_VDI 

2155 update_map[UUID_TAG] = self.uuid 

2156 update_map[NAME_LABEL_TAG] = util.to_plain_string( \ 

2157 self.session.xenapi.VDI.get_name_label(vdi_ref)) 

2158 update_map[NAME_DESCRIPTION_TAG] = util.to_plain_string( \ 

2159 self.session.xenapi.VDI.get_name_description(vdi_ref)) 

2160 update_map[SNAPSHOT_TIME_TAG] = \ 

2161 self.session.xenapi.VDI.get_snapshot_time(vdi_ref) 

2162 update_map[METADATA_OF_POOL_TAG] = \ 

2163 self.session.xenapi.VDI.get_metadata_of_pool(vdi_ref) 

2164 LVMMetadataHandler(self.sr.mdpath).updateMetadata(update_map) 

2165 

2166 def _ensure_cbt_space(self): 

2167 self.sr.ensureCBTSpace() 

2168 

2169 def _create_cbt_log(self): 

2170 logname = self._get_cbt_logname(self.uuid) 

2171 self.sr.lvmCache.create(logname, self.sr.journaler.LV_SIZE, CBTLOG_TAG) 

2172 logpath = super(LVHDVDI, self)._create_cbt_log() 

2173 self.sr.lvmCache.deactivateNoRefcount(logname) 

2174 return logpath 

2175 

2176 def _delete_cbt_log(self): 

2177 logpath = self._get_cbt_logpath(self.uuid) 

2178 if self._cbt_log_exists(logpath): 

2179 logname = self._get_cbt_logname(self.uuid) 

2180 self.sr.lvmCache.remove(logname) 

2181 

2182 def _rename(self, oldpath, newpath): 

2183 oldname = os.path.basename(oldpath) 

2184 newname = os.path.basename(newpath) 

2185 self.sr.lvmCache.rename(oldname, newname) 

2186 

2187 def update_slaves_on_cbt_disable(self, cbtlog): 

2188 args = { 

2189 "vgName": self.sr.vgname, 

2190 "action1": "deactivateNoRefcount", 

2191 "lvName1": cbtlog 

2192 } 

2193 

2194 host_refs = util.get_hosts_attached_on(self.session, [self.uuid]) 

2195 

2196 message = f"Deactivating {cbtlog}" 

2197 self.sr.call_on_slave(args, host_refs, message) 

2198 

2199 def _activate_cbt_log(self, lv_name): 

2200 self.sr.lvmCache.refresh() 

2201 if not self.sr.lvmCache.is_active(lv_name): 2201 ↛ 2202line 2201 didn't jump to line 2202, because the condition on line 2201 was never true

2202 try: 

2203 self.sr.lvmCache.activateNoRefcount(lv_name) 

2204 return True 

2205 except Exception as e: 

2206 util.SMlog("Exception in _activate_cbt_log, " 

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

2208 raise 

2209 else: 

2210 return False 

2211 

2212 def _deactivate_cbt_log(self, lv_name): 

2213 try: 

2214 self.sr.lvmCache.deactivateNoRefcount(lv_name) 

2215 except Exception as e: 

2216 util.SMlog("Exception in _deactivate_cbt_log, Error: %s." % str(e)) 

2217 raise 

2218 

2219 def _cbt_log_exists(self, logpath): 

2220 return lvutil.exists(logpath) 

2221 

2222# SR registration at import 

2223SR.registerSR(LVHDSR)