Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/python3 

2# 

3# Copyright (C) Citrix Systems Inc. 

4# 

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

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

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

8# 

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

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

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

12# GNU Lesser General Public License for more details. 

13# 

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

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

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

17# 

18# LVMSR: VHD and QCOW2 on LVM storage repository 

19# 

20 

21from sm_typing import Dict, List, override 

22 

23import SR 

24from SR import deviceCheck 

25import VDI 

26import SRCommand 

27import util 

28import lvutil 

29import lvmcache 

30import scsiutil 

31import lock 

32import os 

33import sys 

34import time 

35import errno 

36import xs_errors 

37import cleanup 

38import blktap2 

39from journaler import Journaler 

40from refcounter import RefCounter 

41from ipc import IPCFlag 

42from constants import NS_PREFIX_LVM, VG_LOCATION, VG_PREFIX, CBT_BLOCK_SIZE 

43from cowutil import CowUtil, getCowUtil, getImageStringFromVdiType, getVdiTypeFromImageFormat 

44from lvmcowutil import LV_PREFIX, LvmCowUtil 

45from lvmanager import LVActivator 

46from vditype import VdiType 

47import XenAPI # pylint: disable=import-error 

48import re 

49from srmetadata import ALLOCATION_TAG, NAME_LABEL_TAG, NAME_DESCRIPTION_TAG, \ 

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

51 READ_ONLY_TAG, MANAGED_TAG, SNAPSHOT_TIME_TAG, METADATA_OF_POOL_TAG, \ 

52 LVMMetadataHandler, METADATA_OBJECT_TYPE_VDI, \ 

53 METADATA_OBJECT_TYPE_SR, METADATA_UPDATE_OBJECT_TYPE_TAG 

54from metadata import retrieveXMLfromFile, _parseXML 

55from xmlrpc.client import DateTime 

56import glob 

57from constants import CBTLOG_TAG 

58from fairlock import Fairlock 

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

60 

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

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

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

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

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

66 "VDI_ACTIVATE", "VDI_DEACTIVATE"] 

67 

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

69 

70DRIVER_INFO = { 

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

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

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

74 'vendor': 'XenSource Inc', 

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

76 'driver_version': '1.0', 

77 'required_api_version': '1.0', 

78 'capabilities': CAPABILITIES, 

79 'configuration': CONFIGURATION 

80 } 

81 

82OPS_EXCLUSIVE = [ 

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

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

85 "vdi_clone"] 

86 

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

88LONG_SNAPTIME = 60 

89 

90class LVMSR(SR.SR): 

91 DRIVER_TYPE = 'lvhd' 

92 

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

94 PROVISIONING_DEFAULT = "thick" 

95 THIN_PLUGIN = "lvhd-thin" 

96 

97 PLUGIN_ON_SLAVE = "on-slave" 

98 

99 FLAG_USE_VHD = "use_vhd" 

100 MDVOLUME_NAME = "MGT" 

101 

102 ALLOCATION_QUANTUM = "allocation_quantum" 

103 INITIAL_ALLOCATION = "initial_allocation" 

104 

105 LOCK_RETRY_INTERVAL = 3 

106 LOCK_RETRY_ATTEMPTS = 10 

107 

108 TEST_MODE_KEY = "testmode" 

109 TEST_MODE_VHD_FAIL_REPARENT_BEGIN = "vhd_fail_reparent_begin" 

110 TEST_MODE_VHD_FAIL_REPARENT_LOCATOR = "vhd_fail_reparent_locator" 

111 TEST_MODE_VHD_FAIL_REPARENT_END = "vhd_fail_reparent_end" 

112 TEST_MODE_VHD_FAIL_RESIZE_BEGIN = "vhd_fail_resize_begin" 

113 TEST_MODE_VHD_FAIL_RESIZE_DATA = "vhd_fail_resize_data" 

114 TEST_MODE_VHD_FAIL_RESIZE_METADATA = "vhd_fail_resize_metadata" 

115 TEST_MODE_VHD_FAIL_RESIZE_END = "vhd_fail_resize_end" 

116 

117 ENV_VAR_VHD_TEST = { 

118 TEST_MODE_VHD_FAIL_REPARENT_BEGIN: 

119 "VHD_UTIL_TEST_FAIL_REPARENT_BEGIN", 

120 TEST_MODE_VHD_FAIL_REPARENT_LOCATOR: 

121 "VHD_UTIL_TEST_FAIL_REPARENT_LOCATOR", 

122 TEST_MODE_VHD_FAIL_REPARENT_END: 

123 "VHD_UTIL_TEST_FAIL_REPARENT_END", 

124 TEST_MODE_VHD_FAIL_RESIZE_BEGIN: 

125 "VHD_UTIL_TEST_FAIL_RESIZE_BEGIN", 

126 TEST_MODE_VHD_FAIL_RESIZE_DATA: 

127 "VHD_UTIL_TEST_FAIL_RESIZE_DATA_MOVED", 

128 TEST_MODE_VHD_FAIL_RESIZE_METADATA: 

129 "VHD_UTIL_TEST_FAIL_RESIZE_METADATA_MOVED", 

130 TEST_MODE_VHD_FAIL_RESIZE_END: 

131 "VHD_UTIL_TEST_FAIL_RESIZE_END" 

132 } 

133 testMode = "" 

134 

135 legacyMode = True 

136 

137 @override 

138 @staticmethod 

139 def handles(type) -> bool: 

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

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

142 if __name__ == '__main__': 

143 name = sys.argv[0] 

144 else: 

145 name = __name__ 

146 if name.endswith("LVMSR"): 

147 return type == "lvm" 

148 elif name.endswith("EXTSR"): 

149 return type == "ext" 

150 return type == LVMSR.DRIVER_TYPE 

151 

152 def __init__(self, srcmd, sr_uuid): 

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

154 self._init_image_formats() 

155 

156 @override 

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

158 self.ops_exclusive = OPS_EXCLUSIVE 

159 

160 self.isMaster = False 

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

162 self.isMaster = True 

163 

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

165 self.sr_vditype = SR.DEFAULT_TAP 

166 self.uuid = sr_uuid 

167 self.vgname = VG_PREFIX + self.uuid 

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

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

170 self.provision = self.PROVISIONING_DEFAULT 

171 

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

173 if has_sr_ref: 

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

175 else: 

176 self.other_conf = None 

177 

178 self.lvm_conf = None 

179 if self.other_conf: 

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

181 

182 try: 

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

184 except: 

185 raise xs_errors.XenError('SRUnavailable', \ 

186 opterr='Failed to initialise the LVMCache') 

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

188 self.journaler = Journaler(self.lvmCache) 

189 if not has_sr_ref: 

190 return # must be a probe call 

191 # Test for thick vs thin provisioning conf parameter 

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

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

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

195 else: 

196 raise xs_errors.XenError('InvalidArg', \ 

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

198 

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

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

201 self._prepareTestMode() 

202 

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

204 # sm_config flag overrides PBD, if any 

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

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

207 

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

209 self.legacyMode = False 

210 

211 if lvutil._checkVG(self.vgname): 

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

213 "vdi_activate", "vdi_deactivate"]: 

214 self._undoAllJournals() 

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

216 self._checkMetadataVolume() 

217 

218 self.mdexists = False 

219 

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

221 contains_uuid_regex = \ 

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

223 self.storageVDIs = {} 

224 

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

226 # if the lvname has a uuid in it 

227 type = None 

228 vdi = None 

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

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

231 if key.startswith(prefix): 

232 vdi = key[len(prefix):] 

233 self.storageVDIs[vdi] = vdi_type 

234 break 

235 

236 # check if metadata volume exists 

237 try: 

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

239 except: 

240 pass 

241 

242 @override 

243 def cleanup(self) -> None: 

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

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

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

247 

248 def updateSRMetadata(self, allocation): 

249 try: 

250 # Add SR specific SR metadata 

251 sr_info = \ 

252 {ALLOCATION_TAG: allocation, 

253 UUID_TAG: self.uuid, 

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

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

256 } 

257 

258 vdi_info = {} 

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

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

261 

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

263 if not vdi_type: 

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

265 

266 # Create the VDI entry in the SR metadata 

267 vdi_info[vdi_uuid] = \ 

268 { 

269 UUID_TAG: vdi_uuid, 

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

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

272 IS_A_SNAPSHOT_TAG: \ 

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

274 SNAPSHOT_OF_TAG: \ 

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

276 SNAPSHOT_TIME_TAG: \ 

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

278 TYPE_TAG: \ 

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

280 VDI_TYPE_TAG: \ 

281 vdi_type, 

282 READ_ONLY_TAG: \ 

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

284 METADATA_OF_POOL_TAG: \ 

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

286 MANAGED_TAG: \ 

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

288 } 

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

290 

291 except Exception as e: 

292 raise xs_errors.XenError('MetadataError', \ 

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

294 

295 def syncMetadataAndStorage(self): 

296 try: 

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

298 # then delete it from the metadata 

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

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

301 update_map = {} 

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

303 # delete this from metadata 

304 LVMMetadataHandler(self.mdpath). \ 

305 deleteVdiFromMetadata(vdi_info[vdi][UUID_TAG]) 

306 else: 

307 # search for this in the metadata, compare types 

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

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

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

311 # storage type takes authority 

312 update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] \ 

313 = METADATA_OBJECT_TYPE_VDI 

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

315 update_map[VDI_TYPE_TAG] = \ 

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

317 LVMMetadataHandler(self.mdpath) \ 

318 .updateMetadata(update_map) 

319 else: 

320 # This should never happen 

321 pass 

322 

323 except Exception as e: 

324 raise xs_errors.XenError('MetadataError', \ 

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

326 

327 def syncMetadataAndXapi(self): 

328 try: 

329 # get metadata 

330 (sr_info, vdi_info) = \ 

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

332 

333 # First synch SR parameters 

334 self.update(self.uuid) 

335 

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

337 for vdi_offset in vdi_info.keys(): 

338 try: 

339 vdi_ref = \ 

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

341 vdi_info[vdi_offset][UUID_TAG]) 

342 except: 

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

344 continue 

345 

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

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

348 

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

350 vdi_info[vdi_offset][NAME_DESCRIPTION_TAG] != \ 

351 new_name_description: 

352 update_map = {} 

353 update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] = \ 

354 METADATA_OBJECT_TYPE_VDI 

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

356 update_map[NAME_LABEL_TAG] = new_name_label 

357 update_map[NAME_DESCRIPTION_TAG] = new_name_description 

358 LVMMetadataHandler(self.mdpath) \ 

359 .updateMetadata(update_map) 

360 except Exception as e: 

361 raise xs_errors.XenError('MetadataError', \ 

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

363 

364 def _checkMetadataVolume(self): 

365 util.SMlog("Entering _checkMetadataVolume") 

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

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

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

369 try: 

370 # activate the management volume 

371 # will be deactivated at detach time 

372 self.lvmCache.activateNoRefcount(self.MDVOLUME_NAME) 

373 self._synchSmConfigWithMetaData() 

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

375 self.syncMetadataAndStorage() 

376 self.syncMetadataAndXapi() 

377 except Exception as e: 

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

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

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

381 self._introduceMetaDataVolume() 

382 

383 if self.mdexists: 

384 self.legacyMode = False 

385 

386 def _synchSmConfigWithMetaData(self): 

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

388 

389 try: 

390 # get SR info from metadata 

391 sr_info = {} 

392 map = {} 

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

394 

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

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

397 

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

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

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

401 else: 

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

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

404 

405 except Exception as e: 

406 raise xs_errors.XenError( 

407 'MetadataError', 

408 opterr='Error reading SR params from ' 

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

410 try: 

411 map[self.FLAG_USE_VHD] = 'true' 

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

413 except: 

414 raise xs_errors.XenError( 

415 'MetadataError', 

416 opterr='Error updating sm_config key') 

417 

418 def _introduceMetaDataVolume(self): 

419 util.SMlog("Creating Metadata volume") 

420 try: 

421 config = {} 

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

423 

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

425 self.lvmCache.activateNoRefcount(self.MDVOLUME_NAME) 

426 

427 name_label = util.to_plain_string( \ 

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

429 name_description = util.to_plain_string( \ 

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

431 config[self.FLAG_USE_VHD] = "true" 

432 config['allocation'] = self.provision 

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

434 

435 # Add the SR metadata 

436 self.updateSRMetadata(self.provision) 

437 except Exception as e: 

438 raise xs_errors.XenError('MetadataError', \ 

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

440 

441 def _removeMetadataVolume(self): 

442 if self.mdexists: 

443 try: 

444 self.lvmCache.remove(self.MDVOLUME_NAME) 

445 except: 

446 raise xs_errors.XenError('MetadataError', \ 

447 opterr='Failed to delete MGT Volume') 

448 

449 def _refresh_size(self): 

450 """ 

451 Refreshs the size of the backing device. 

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

453 """ 

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

455 # LVMoHBASR, LVMoISCSISR 

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

457 else: 

458 # LVMSR 

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

460 scsiutil.refreshdev(devices) 

461 return True 

462 

463 def _expand_size(self): 

464 """ 

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

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

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

468 """ 

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

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

471 resizethreshold = 100 * 1024 * 1024 # 100MB 

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

473 totaldevicesize = 0 

474 for device in devices: 

475 totaldevicesize = totaldevicesize + scsiutil.getsize(device) 

476 if totaldevicesize >= (currentvgsize + resizethreshold): 

477 try: 

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

479 # LVMoHBASR, LVMoISCSISR might have slaves 

480 scsiutil.refresh_lun_size_by_SCSIid_on_slaves(self.session, 

481 getattr(self, 'SCSIid')) 

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

483 self.uuid) 

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

485 lvutil.resizePV(pv) 

486 except: 

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

488 " the PV" % self.uuid) 

489 

490 @override 

491 @deviceCheck 

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

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

494 if not self.isMaster: 

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

496 raise xs_errors.XenError('LVMMaster') 

497 

498 if lvutil._checkVG(self.vgname): 

499 raise xs_errors.XenError('SRExists') 

500 

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

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

503 raise xs_errors.XenError('SRInUse') 

504 

505 # Check serial number entry in SR records 

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

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

508 raise xs_errors.XenError('SRInUse') 

509 

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

511 

512 #Update serial number string 

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

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

515 

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

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

518 self.FLAG_USE_VHD, 'true') 

519 

520 @override 

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

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

523 if not self.isMaster: 

524 raise xs_errors.XenError('LVMMaster') 

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

526 

527 success = True 

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

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

530 continue 

531 

532 if util.doesFileHaveOpenHandles(fileName): 

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

534 "handles" % fileName) 

535 success = False 

536 continue 

537 

538 # Now attempt to remove the dev mapper entry 

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

540 success = False 

541 continue 

542 

543 try: 

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

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

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

547 os.unlink(lpath) 

548 except OSError as e: 

549 if e.errno != errno.ENOENT: 

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

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

552 success = False 

553 

554 if success: 

555 try: 

556 if util.pathexists(self.path): 

557 os.rmdir(self.path) 

558 except Exception as e: 

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

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

561 success = False 

562 

563 self._removeMetadataVolume() 

564 self.lvmCache.refresh() 

565 if LvmCowUtil.getVolumeInfo(self.lvmCache): 

566 raise xs_errors.XenError('SRNotEmpty') 

567 

568 if not success: 

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

570 "for details.") 

571 

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

573 self._cleanup() 

574 

575 @override 

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

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

578 

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

580 

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

582 raise xs_errors.XenError('SRUnavailable', \ 

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

584 

585 # Refresh the metadata status 

586 self._checkMetadataVolume() 

587 

588 refreshsizeok = self._refresh_size() 

589 

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

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

592 self._expand_size() 

593 

594 # Update SCSIid string 

595 util.SMlog("Calling devlist_to_serial") 

596 scsiutil.add_serial_record( 

597 self.session, self.sr_ref, 

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

599 

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

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

602 vdiInfo = LvmCowUtil.getVDIInfo(self.lvmCache) 

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

604 if VdiType.isCowImage(info.vdiType): 

605 self.legacyMode = False 

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

607 self._introduceMetaDataVolume() 

608 break 

609 

610 # Set the block scheduler 

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

612 self.block_setscheduler(dev) 

613 

614 @override 

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

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

617 cleanup.abort(self.uuid) 

618 

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

620 # go through all devmapper entries for this VG 

621 success = True 

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

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

624 continue 

625 

626 with Fairlock('devicemapper'): 

627 # check if any file has open handles 

628 if util.doesFileHaveOpenHandles(fileName): 

629 # if yes, log this and signal failure 

630 util.SMlog( 

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

632 "open handles") 

633 success = False 

634 continue 

635 

636 # Now attempt to remove the dev mapper entry 

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

638 success = False 

639 continue 

640 

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

642 try: 

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

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

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

646 util.force_unlink(lvname) 

647 except Exception as e: 

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

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

650 success = False 

651 

652 # now remove the directory where the symlinks are 

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

654 if success: 

655 try: 

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

657 os.rmdir(self.path) 

658 except Exception as e: 

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

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

661 success = False 

662 

663 if not success: 

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

665 "for details.") 

666 

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

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

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

670 # only place to do so. 

671 self._cleanup(self.isMaster) 

672 

673 @override 

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

675 if not self.legacyMode: 

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

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

678 

679 @override 

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

681 activated_lvs = set() 

682 try: 

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

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

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

686 raise xs_errors.XenError('LVMMaster') 

687 

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

689 self._expand_size() 

690 self.lvmCache.refresh() 

691 cbt_vdis = self.lvmCache.getTagged(CBTLOG_TAG) 

692 self._loadvdis() 

693 stats = lvutil._getVGstats(self.vgname) 

694 self.physical_size = stats['physical_size'] 

695 self.physical_utilisation = stats['physical_utilisation'] 

696 

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

698 # XAPI 

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

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

701 # get VDIs from XAPI 

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

703 vdi_uuids = set([]) 

704 for vdi in vdis: 

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

706 

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

708 

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

710 vdi_uuid = info[vdi][UUID_TAG] 

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

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

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

714 else: 

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

716 

717 if vdi_uuid not in vdi_uuids: 

718 vdi_type = info[vdi][VDI_TYPE_TAG] 

719 sm_config = {} 

720 sm_config['vdi_type'] = vdi_type 

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

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

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

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

725 "removing from metadata." % 

726 (vdi_uuid, lvname)) 

727 LVMMetadataHandler(self.mdpath). \ 

728 deleteVdiFromMetadata(vdi_uuid) 

729 continue 

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

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

732 self.lvActivator.activate( 

733 vdi_uuid, lvname, LVActivator.NORMAL) 

734 activated_lvs.add(vdi_uuid) 

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

736 

737 if not VdiType.isCowImage(vdi_type): 

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

739 utilisation = \ 

740 util.roundup(lvutil.LVM_SIZE_INCREMENT, 

741 int(size)) 

742 else: 

743 cowutil = getCowUtil(vdi_type) 

744 lvmcowutil = LvmCowUtil(cowutil) 

745 

746 parent = cowutil.getParentNoCheck(lvPath) 

747 

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

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

750 size = cowutil.getSizeVirt(lvPath) 

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

752 utilisation = util.roundup( 

753 lvutil.LVM_SIZE_INCREMENT, 

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

755 ) 

756 else: 

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

758 

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

760 vdi_uuid, 

761 info[vdi][NAME_LABEL_TAG], 

762 info[vdi][NAME_DESCRIPTION_TAG], 

763 self.sr_ref, 

764 info[vdi][TYPE_TAG], 

765 False, 

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

767 {}, 

768 vdi_uuid, 

769 {}, 

770 sm_config) 

771 

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

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

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

775 str(size)) 

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

777 vdi_ref, str(utilisation)) 

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

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

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

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

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

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

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

785 vdi_ref, info[vdi][METADATA_OF_POOL_TAG]) 

786 

787 # Update CBT status of disks either just added 

788 # or already in XAPI 

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

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

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

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

793 # For existing VDIs, update local state too 

794 # Scan in base class SR updates existing VDIs 

795 # again based on local states 

796 if vdi_uuid in self.vdis: 

797 self.vdis[vdi_uuid].cbt_enabled = True 

798 cbt_vdis.remove(cbt_logname) 

799 

800 # Now set the snapshot statuses correctly in XAPI 

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

802 try: 

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

804 except: 

805 # the source VDI no longer exists, continue 

806 continue 

807 

808 for snapvdi in vdiToSnaps[srcvdi]: 

809 try: 

810 # this might fail in cases where its already set 

811 snapref = \ 

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

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

814 except Exception as e: 

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

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

817 

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

819 # If we have items remaining in this list, 

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

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

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

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

824 new_vdi = self.vdi(cbt_uuid) 

825 new_vdi.ty = "cbt_metadata" 

826 new_vdi.cbt_enabled = True 

827 self.vdis[cbt_uuid] = new_vdi 

828 

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

830 self._kickGC() 

831 

832 finally: 

833 for vdi in activated_lvs: 

834 self.lvActivator.deactivate( 

835 vdi, LVActivator.NORMAL, False) 

836 

837 @override 

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

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

840 return 

841 self._updateStats(uuid, 0) 

842 

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

844 return 

845 

846 # synch name_label in metadata with XAPI 

847 update_map = {} 

848 update_map = {METADATA_UPDATE_OBJECT_TYPE_TAG: \ 

849 METADATA_OBJECT_TYPE_SR, 

850 NAME_LABEL_TAG: util.to_plain_string( \ 

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

852 NAME_DESCRIPTION_TAG: util.to_plain_string( \ 

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

854 } 

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

856 

857 def _updateStats(self, uuid, virtAllocDelta): 

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

859 self.virtual_allocation = valloc + virtAllocDelta 

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

861 (uuid, self.virtual_allocation)) 

862 stats = lvutil._getVGstats(self.vgname) 

863 self.physical_size = stats['physical_size'] 

864 self.physical_utilisation = stats['physical_utilisation'] 

865 self._db_update() 

866 

867 @override 

868 @deviceCheck 

869 def probe(self) -> str: 

870 return lvutil.srlist_toxml( 

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

872 VG_PREFIX, 

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

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

875 

876 @override 

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

878 return LVMVDI(self, uuid) 

879 

880 def _loadvdis(self): 

881 self.virtual_allocation = 0 

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

883 self.allVDIs = {} 

884 

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

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

887 continue 

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

889 raise xs_errors.XenError('VDIUnavailable', \ 

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

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

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

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

894 

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

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

897 if vdi.parent in self.vdis: 

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

899 if vdi.parent in geneology: 

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

901 else: 

902 geneology[vdi.parent] = [uuid] 

903 

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

905 # will be GC'ed 

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

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

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

909 del self.vdis[uuid] 

910 

911 def _ensureSpaceAvailable(self, amount_needed): 

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

913 if (space_available < amount_needed): 

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

915 (space_available, amount_needed)) 

916 raise xs_errors.XenError('SRNoSpace') 

917 

918 def _handleInterruptedCloneOps(self): 

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

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

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

922 self._handleInterruptedCloneOp(uuid, val) 

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

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

925 

926 def _handleInterruptedCoalesceLeaf(self): 

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

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

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

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

931 self.lvmCache.refresh() 

932 

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

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

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

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

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

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

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

940 lvs = LvmCowUtil.getVolumeInfo(self.lvmCache) 

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

942 

943 # is there a "base copy" VDI? 

944 if not lvs.get(baseUuid): 

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

946 if lvs.get(origUuid): 

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

948 return 

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

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

951 

952 vdis = LvmCowUtil.getVDIInfo(self.lvmCache) 

953 base = vdis[baseUuid] 

954 cowutil = getCowUtil(base.vdiType) 

955 

956 if forceUndo: 

957 util.SMlog("Explicit revert") 

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

959 return 

960 

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

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

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

964 return 

965 

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

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

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

969 return 

970 

971 orig = vdis[origUuid] 

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

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

974 if orig.parentUuid != baseUuid: 

975 parent = vdis[orig.parentUuid] 

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

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

978 

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

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

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

982 return 

983 

984 if clonUuid: 

985 clon = vdis[clonUuid] 

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

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

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

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

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

991 return 

992 

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

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

995 

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

997 base = lvs[baseUuid] 

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

999 

1000 # make the parent RW 

1001 if base.readonly: 

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

1003 

1004 ns = NS_PREFIX_LVM + self.uuid 

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

1006 origRefcountNormal = 0 

1007 

1008 # un-hide the parent 

1009 if VdiType.isCowImage(base.vdiType): 

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

1011 origRefcountNormal = 1 

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

1013 if imageInfo.hidden: 

1014 cowutil.setHidden(basePath, False) 

1015 elif base.hidden: 

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

1017 

1018 # remove the child nodes 

1019 if clonUuid and lvs.get(clonUuid): 

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

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

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

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

1024 self.lvActivator.remove(clonUuid, False) 

1025 if lvs.get(origUuid): 

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

1027 

1028 # inflate the parent to fully-allocated size 

1029 if VdiType.isCowImage(base.vdiType): 

1030 lvmcowutil = LvmCowUtil(cowutil) 

1031 fullSize = lvmcowutil.calcVolumeSize(imageInfo.sizeVirt) 

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

1033 

1034 # rename back 

1035 origLV = LV_PREFIX[base.vdiType] + origUuid 

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

1037 RefCounter.reset(baseUuid, ns) 

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

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

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

1041 

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

1043 # flag to facilitate vm deactivate 

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

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

1046 

1047 # update LVM metadata on slaves 

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

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

1050 origLV, origUuid, slaves) 

1051 

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

1053 

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

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

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

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

1058 would cause a deadlock)""" 

1059 base = vdis[baseUuid] 

1060 clon = None 

1061 if clonUuid: 

1062 clon = vdis[clonUuid] 

1063 

1064 cleanup.abort(self.uuid) 

1065 

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

1067 if not base.hidden: 

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

1069 self.lvmCache.setHidden(base.lvName) 

1070 else: 

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

1072 cowutil.setHidden(basePath) 

1073 if not base.lvReadonly: 

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

1075 

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

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

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

1079 

1080 # Update the original record 

1081 try: 

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

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

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

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

1086 sm_config['vhd-parent'] = baseUuid 

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

1088 except XenAPI.Failure: 

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

1090 

1091 # introduce the new VDI records 

1092 if clonUuid: 

1093 try: 

1094 clon_vdi = VDI.VDI(self, clonUuid) 

1095 clon_vdi.read_only = False 

1096 clon_vdi.location = clonUuid 

1097 clon_vdi.utilisation = clon.sizeLV 

1098 clon_vdi.sm_config = { 

1099 "vdi_type": clon.vdiType, 

1100 "vhd-parent": baseUuid} 

1101 

1102 if not self.legacyMode: 

1103 LVMMetadataHandler(self.mdpath). \ 

1104 ensureSpaceIsAvailableForVdis(1) 

1105 

1106 clon_vdi_ref = clon_vdi._db_introduce() 

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

1108 (clon_vdi_ref, clonUuid)) 

1109 

1110 vdi_info = {UUID_TAG: clonUuid, 

1111 NAME_LABEL_TAG: clon_vdi.label, 

1112 NAME_DESCRIPTION_TAG: clon_vdi.description, 

1113 IS_A_SNAPSHOT_TAG: 0, 

1114 SNAPSHOT_OF_TAG: '', 

1115 SNAPSHOT_TIME_TAG: '', 

1116 TYPE_TAG: type, 

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

1118 READ_ONLY_TAG: int(clon_vdi.read_only), 

1119 MANAGED_TAG: int(clon_vdi.managed), 

1120 METADATA_OF_POOL_TAG: '' 

1121 } 

1122 

1123 if not self.legacyMode: 

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

1125 

1126 except XenAPI.Failure: 

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

1128 

1129 try: 

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

1131 base_vdi.label = "base copy" 

1132 base_vdi.read_only = True 

1133 base_vdi.location = baseUuid 

1134 base_vdi.size = base.sizeVirt 

1135 base_vdi.utilisation = base.sizeLV 

1136 base_vdi.managed = False 

1137 base_vdi.sm_config = { 

1138 "vdi_type": base.vdiType, 

1139 "vhd-parent": baseUuid} 

1140 

1141 if not self.legacyMode: 

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

1143 

1144 base_vdi_ref = base_vdi._db_introduce() 

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

1146 (base_vdi_ref, baseUuid)) 

1147 

1148 vdi_info = {UUID_TAG: baseUuid, 

1149 NAME_LABEL_TAG: base_vdi.label, 

1150 NAME_DESCRIPTION_TAG: base_vdi.description, 

1151 IS_A_SNAPSHOT_TAG: 0, 

1152 SNAPSHOT_OF_TAG: '', 

1153 SNAPSHOT_TIME_TAG: '', 

1154 TYPE_TAG: type, 

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

1156 READ_ONLY_TAG: int(base_vdi.read_only), 

1157 MANAGED_TAG: int(base_vdi.managed), 

1158 METADATA_OF_POOL_TAG: '' 

1159 } 

1160 

1161 if not self.legacyMode: 

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

1163 except XenAPI.Failure: 

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

1165 

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

1167 

1168 def _undoAllJournals(self): 

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

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

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

1172 # ops might require inflations 

1173 self.lock.acquire() 

1174 try: 

1175 self._undoAllInflateJournals() 

1176 self._undoAllCowJournals() 

1177 self._handleInterruptedCloneOps() 

1178 self._handleInterruptedCoalesceLeaf() 

1179 finally: 

1180 self.lock.release() 

1181 self.cleanup() 

1182 

1183 def _undoAllInflateJournals(self): 

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

1185 if len(entries) == 0: 

1186 return 

1187 self._loadvdis() 

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

1189 vdi = self.vdis.get(uuid) 

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

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

1192 (uuid, vdi.path, val)) 

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

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

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

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

1197 

1198 cowutil = getCowUtil(vdi.vdi_type) 

1199 lvmcowutil = LvmCowUtil(cowutil) 

1200 

1201 footer_size = cowutil.getFooterSize() 

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

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

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

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

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

1207 LvmCowUtil.refreshVolumeOnAllSlaves( 

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

1209 ) 

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

1211 delattr(self, "vdiInfo") 

1212 delattr(self, "allVDIs") 

1213 

1214 def _undoAllCowJournals(self): 

1215 """ 

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

1217 """ 

1218 journals = LvmCowUtil.getAllResizeJournals(self.lvmCache) 

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

1220 return 

1221 self._loadvdis() 

1222 

1223 for uuid, jlvName in journals: 

1224 vdi = self.vdis[uuid] 

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

1226 cowutil = getCowUtil(vdi.vdi_type) 

1227 lvmcowutil = LvmCowUtil(cowutil) 

1228 

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

1230 self.lvmCache.activateNoRefcount(jlvName) 

1231 fullSize = lvmcowutil.calcVolumeSize(vdi.size) 

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

1233 try: 

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

1235 cowutil.revert(vdi.path, jFile) 

1236 except util.CommandException: 

1237 util.logException("COW journal revert") 

1238 cowutil.check(vdi.path) 

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

1240 # Attempt to reclaim unused space 

1241 

1242 

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

1244 NewSize = lvmcowutil.calcVolumeSize(imageInfo.sizeVirt) 

1245 if NewSize < fullSize: 

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

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

1248 self.lvmCache.remove(jlvName) 

1249 delattr(self, "vdiInfo") 

1250 delattr(self, "allVDIs") 

1251 

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

1253 master_ref = util.get_this_host_ref(self.session) 

1254 for hostRef in host_refs: 

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

1256 continue 

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

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

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

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

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

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

1263 

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

1265 baseUuid, baseLV): 

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

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

1268 for the base LV""" 

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

1270 "action1": "refresh", 

1271 "lvName1": origLV, 

1272 "action2": "activate", 

1273 "ns2": NS_PREFIX_LVM + self.uuid, 

1274 "lvName2": baseLV, 

1275 "uuid2": baseUuid} 

1276 

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

1278 self.call_on_slave(args, hostRefs, message) 

1279 

1280 def _updateSlavesOnCBTClone(self, hostRefs, cbtlog): 

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

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

1283 "action1": "deactivateNoRefcount", 

1284 "lvName1": cbtlog, 

1285 "action2": "refresh", 

1286 "lvName2": cbtlog} 

1287 

1288 message = f"Updating {cbtlog}" 

1289 self.call_on_slave(args, hostRefs, message) 

1290 

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

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

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

1294 "action1": "cleanupLockAndRefcount", 

1295 "uuid1": baseUuid, 

1296 "ns1": NS_PREFIX_LVM + self.uuid} 

1297 

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

1299 self.call_on_slave(args, hostRefs, message) 

1300 

1301 def _deactivateOnSlave(self, hostRefs, lvname): 

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

1303 args = { 

1304 "vgName": self.vgname, 

1305 "action1": "deactivateNoRefcount", 

1306 "lvName1": lvname} 

1307 

1308 message = f"Deactivating {lvname}" 

1309 self.call_on_slave(args, hostRefs, message) 

1310 

1311 def _cleanup(self, skipLockCleanup=False): 

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

1313 RefCounter.resetAll(NS_PREFIX_LVM + self.uuid) 

1314 IPCFlag(self.uuid).clearAll() 

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

1316 lock.Lock.cleanupAll(self.uuid) 

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

1318 

1319 def _prepareTestMode(self): 

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

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

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

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

1324 

1325 def _kickGC(self): 

1326 util.SMlog("Kicking GC") 

1327 cleanup.start_gc_service(self.uuid) 

1328 

1329 def ensureCBTSpace(self, virtual_size=0): 

1330 # Ensure we have space for at least one LV 

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

1332 self._ensureSpaceAvailable(size) 

1333 

1334 

1335class LVMVDI(VDI.VDI): 

1336 

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

1338 

1339 @override 

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

1341 self.lock = self.sr.lock 

1342 self.lvActivator = self.sr.lvActivator 

1343 self.loaded = False 

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

1345 self._setType(VdiType.RAW) 

1346 self.uuid = vdi_uuid 

1347 self.location = self.uuid 

1348 self.exists = True 

1349 

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

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

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

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

1354 else: 

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

1356 return 

1357 

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

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

1360 return 

1361 

1362 image_format = None 

1363 

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

1365 self.exists = False 

1366 

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

1368 if vdi_sm_config: 

1369 image_format = self.sr.read_config_image_format(vdi_sm_config) 

1370 

1371 if not image_format: 

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

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

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

1375 # We need it to validate the vdi_type choice 

1376 for image_format in self.sr.preferred_image_formats: 

1377 vdi_type = self.sr._resolve_vdi_type_from_image_format(image_format) 

1378 self._setType(vdi_type) 

1379 try: 

1380 self.cowutil.validateAndRoundImageSize(size) 

1381 break 

1382 except xs_errors.SROSError: 

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

1384 else: 

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

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

1387 image_format = self.sr.preferred_image_formats[0] 

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

1389 

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

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

1392 

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

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

1395 

1396 @override 

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

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

1399 if not self.sr.isMaster: 

1400 raise xs_errors.XenError('LVMMaster') 

1401 if self.exists: 

1402 raise xs_errors.XenError('VDIExists') 

1403 

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

1405 

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

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

1408 lvSize = 0 

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

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

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

1412 else: 

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

1414 lvSize = util.roundup( 

1415 lvutil.LVM_SIZE_INCREMENT, 

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

1417 ) 

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

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

1420 

1421 self.sr._ensureSpaceAvailable(lvSize) 

1422 

1423 try: 

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

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

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

1427 else: 

1428 self.cowutil.create( 

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

1430 ) 

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

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

1433 except util.CommandException as e: 

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

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

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

1437 

1438 self.utilisation = lvSize 

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

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

1441 

1442 if not self.sr.legacyMode: 

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

1444 

1445 self.ref = self._db_introduce() 

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

1447 

1448 vdi_info = {UUID_TAG: self.uuid, 

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

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

1451 IS_A_SNAPSHOT_TAG: 0, 

1452 SNAPSHOT_OF_TAG: '', 

1453 SNAPSHOT_TIME_TAG: '', 

1454 TYPE_TAG: self.ty, 

1455 VDI_TYPE_TAG: self.vdi_type, 

1456 READ_ONLY_TAG: int(self.read_only), 

1457 MANAGED_TAG: int(self.managed), 

1458 METADATA_OF_POOL_TAG: '' 

1459 } 

1460 

1461 if not self.sr.legacyMode: 

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

1463 

1464 return VDI.VDI.get_params(self) 

1465 

1466 @override 

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

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

1469 try: 

1470 self._loadThis() 

1471 except xs_errors.SRException as e: 

1472 # Catch 'VDI doesn't exist' exception 

1473 if e.errno == 46: 

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

1475 raise 

1476 

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

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

1479 raise xs_errors.XenError("VDIDelete", \ 

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

1481 

1482 if not self.hidden: 

1483 self._markHidden() 

1484 

1485 if not data_only: 

1486 # Remove from XAPI and delete from MGT 

1487 self._db_forget() 

1488 else: 

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

1490 # Only delete from MGT 

1491 if not self.sr.legacyMode: 

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

1493 

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

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

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

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

1498 

1499 try: 

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

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

1502 self.sr.lock.cleanupAll(vdi_uuid) 

1503 except xs_errors.SRException as e: 

1504 util.SMlog( 

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

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

1507 

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

1509 self.sr._kickGC() 

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

1511 

1512 @override 

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

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

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

1516 raise xs_errors.XenError('VDIUnavailable', 

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

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

1519 

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

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

1522 needInflate = True 

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

1524 needInflate = False 

1525 else: 

1526 self._loadThis() 

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

1528 needInflate = False 

1529 

1530 if needInflate: 

1531 try: 

1532 self._prepareThin(True, self.vdi_type) 

1533 except: 

1534 util.logException("attach") 

1535 raise xs_errors.XenError('LVMProvisionAttach') 

1536 

1537 try: 

1538 return self._attach() 

1539 finally: 

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

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

1542 

1543 @override 

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

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

1546 self._loadThis() 

1547 already_deflated = (self.utilisation < \ 

1548 self.lvmcowutil.calcVolumeSize(self.size)) 

1549 needDeflate = True 

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

1551 needDeflate = False 

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

1553 needDeflate = False 

1554 # except for snapshots, which are always deflated 

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

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

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

1558 if snap: 

1559 needDeflate = True 

1560 

1561 if needDeflate: 

1562 try: 

1563 self._prepareThin(False, self.vdi_type) 

1564 except: 

1565 util.logException("_prepareThin") 

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

1567 

1568 try: 

1569 self._detach() 

1570 finally: 

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

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

1573 

1574 # We only support offline resize 

1575 @override 

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

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

1578 if not self.sr.isMaster: 

1579 raise xs_errors.XenError('LVMMaster') 

1580 

1581 self._loadThis() 

1582 if self.hidden: 

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

1584 

1585 if size < self.size: 

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

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

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

1589 

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

1591 

1592 if size == self.size: 

1593 return VDI.VDI.get_params(self) 

1594 

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

1596 lvSizeOld = self.size 

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

1598 else: 

1599 lvSizeOld = self.utilisation 

1600 lvSizeNew = self.lvmcowutil.calcVolumeSize(size) 

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

1602 # VDI is currently deflated, so keep it deflated 

1603 lvSizeNew = lvSizeOld 

1604 assert(lvSizeNew >= lvSizeOld) 

1605 spaceNeeded = lvSizeNew - lvSizeOld 

1606 self.sr._ensureSpaceAvailable(spaceNeeded) 

1607 

1608 oldSize = self.size 

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

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

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

1612 self.utilisation = self.size 

1613 else: 

1614 if lvSizeNew != lvSizeOld: 

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

1616 if self.vdi_type == VdiType.QCOW2: 

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

1618 self._chainSetActive(True, True) 

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

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

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

1622 

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

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

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

1626 str(self.utilisation)) 

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

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

1629 if self.vdi_type == VdiType.QCOW2: 

1630 self._chainSetActive(False, True) 

1631 return VDI.VDI.get_params(self) 

1632 

1633 @override 

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

1635 return self._do_snapshot( 

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

1637 

1638 @override 

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

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

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

1642 raise xs_errors.XenError('Unimplemented') 

1643 

1644 parent_uuid = vdi1 

1645 parent_lvname = LV_PREFIX[self.vdi_type] + parent_uuid 

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

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

1648 

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

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

1651 

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

1653 self.cowutil.setHidden(parent_path) 

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

1655 

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

1657 True): 

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

1659 

1660 util.SMlog("Compose done") 

1661 

1662 def reset_leaf(self, sr_uuid, vdi_uuid): 

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

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

1665 raise xs_errors.XenError('Unimplemented') 

1666 

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

1668 

1669 # safety check 

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

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

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

1673 

1674 self.cowutil.killData(self.path) 

1675 

1676 def _attach(self): 

1677 self._chainSetActive(True, True, True) 

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

1679 raise xs_errors.XenError('VDIUnavailable', \ 

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

1681 

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

1683 self.xenstore_data = {} 

1684 

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

1686 scsiutil.gen_synthetic_page_data(self.uuid))) 

1687 

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

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

1690 

1691 self.attached = True 

1692 self.sr.lvActivator.persist() 

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

1694 

1695 def _detach(self): 

1696 self._chainSetActive(False, True) 

1697 self.attached = False 

1698 

1699 @override 

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

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

1702 # If cbt enabled, save file consistency state 

1703 if cbtlog is not None: 

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

1705 consistency_state = False 

1706 else: 

1707 consistency_state = True 

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

1709 (consistency_state, vdi_uuid)) 

1710 else: 

1711 consistency_state = None 

1712 

1713 pause_time = time.time() 

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

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

1716 

1717 snapResult = None 

1718 try: 

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

1720 except Exception as e1: 

1721 try: 

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

1723 secondary=None) 

1724 except Exception as e2: 

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

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

1727 raise 

1728 self.disable_leaf_on_secondary(vdi_uuid, secondary=secondary) 

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

1730 unpause_time = time.time() 

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

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

1733 (unpause_time - pause_time)) 

1734 return snapResult 

1735 

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

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

1738 

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

1740 raise xs_errors.XenError('LVMMaster') 

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

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

1743 

1744 self._loadThis() 

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

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

1747 

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

1749 

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

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

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

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

1754 raise xs_errors.XenError('Unimplemented', \ 

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

1756 

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

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

1759 self._chainSetActive(True, False) 

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

1761 raise xs_errors.XenError('VDIUnavailable', \ 

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

1763 

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

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

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

1767 raise xs_errors.XenError('VDIUnavailable', \ 

1768 opterr='failed to get COW depth') 

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

1770 raise xs_errors.XenError('SnapshotChainTooLong') 

1771 

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

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

1774 

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

1776 thinpr = util.roundup( 

1777 lvutil.LVM_SIZE_INCREMENT, 

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

1779 ) 

1780 lvSizeOrig = thinpr 

1781 lvSizeClon = thinpr 

1782 

1783 hostRefs = [] 

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

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

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

1787 lvSizeOrig = fullpr 

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

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

1790 lvSizeOrig = fullpr 

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

1792 lvSizeClon = fullpr 

1793 

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

1795 snapType == VDI.SNAPSHOT_INTERNAL): 

1796 lvSizeClon = 0 

1797 

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

1799 # inflate journal (for the failure handling 

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

1801 lvSizeBase = self.size 

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

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

1804 size_req -= (self.utilisation - lvSizeBase) 

1805 self.sr._ensureSpaceAvailable(size_req) 

1806 

1807 if hostRefs: 

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

1809 

1810 baseUuid = util.gen_uuid() 

1811 origUuid = self.uuid 

1812 clonUuid = "" 

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

1814 clonUuid = util.gen_uuid() 

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

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

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

1818 

1819 try: 

1820 # self becomes the "base vdi" 

1821 origOldLV = self.lvname 

1822 baseLV = LV_PREFIX[self.vdi_type] + baseUuid 

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

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

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

1826 self.uuid = baseUuid 

1827 self.lvname = baseLV 

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

1829 self.label = "base copy" 

1830 self.read_only = True 

1831 self.location = self.uuid 

1832 self.managed = False 

1833 

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

1835 # the snapshot volumes to avoid requiring double the space 

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

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

1838 self.utilisation = lvSizeBase 

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

1840 

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

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

1843 snapVDI2 = None 

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

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

1846 # If we have CBT enabled on the VDI, 

1847 # set CBT status for the new snapshot disk 

1848 if cbtlog: 

1849 snapVDI2.cbt_enabled = True 

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

1851 

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

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

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

1855 # the parent before we snapshot it 

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

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

1858 else: 

1859 self.cowutil.setHidden(self.path) 

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

1861 

1862 # set the base copy to ReadOnly 

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

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

1865 

1866 if hostRefs: 

1867 self.sr._updateSlavesOnClone(hostRefs, origOldLV, 

1868 snapVDI.lvname, self.uuid, self.lvname) 

1869 

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

1871 if snapType == VDI.SNAPSHOT_DOUBLE and cbtlog: 

1872 snapVDI._cbt_snapshot(clonUuid, cbt_consistency) 

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

1874 cbtlog_file = self._get_cbt_logname(snapVDI.uuid) 

1875 try: 

1876 self.sr._updateSlavesOnCBTClone(hostRefs, cbtlog_file) 

1877 except: 

1878 alert_name = "VDI_CBT_SNAPSHOT_FAILED" 

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

1880 .format(snapVDI.uuid)) 

1881 snapVDI._disable_cbt_on_error(alert_name, alert_str) 

1882 pass 

1883 

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

1885 util.logException("LVMVDI._snapshot") 

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

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

1888 

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

1890 

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

1892 

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

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

1895 

1896 snapLV = LV_PREFIX[snapVdiType] + snapUuid 

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

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

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

1900 if isNew: 

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

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

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

1904 self.cowutil.snapshot( 

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

1906 ) 

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

1908 

1909 snapVDI = LVMVDI(self.sr, snapUuid) 

1910 snapVDI.read_only = False 

1911 snapVDI.location = snapUuid 

1912 snapVDI.size = self.size 

1913 snapVDI.utilisation = snapSizeLV 

1914 snapVDI.sm_config = dict() 

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

1916 if key not in [ 

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

1918 not key.startswith("host_"): 

1919 snapVDI.sm_config[key] = val 

1920 snapVDI.sm_config["vdi_type"] = snapVdiType 

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

1922 # TODO: fix the raw snapshot case  

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

1924 snapVDI.lvname = snapLV 

1925 return snapVDI 

1926 

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

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

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

1930 basePresent = True 

1931 

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

1933 # unused 

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

1935 snap2Parent = "" 

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

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

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

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

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

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

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

1943 

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

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

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

1947 if hostRefs: 

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

1949 

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

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

1952 if hostRefs: 

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

1954 basePresent = False 

1955 else: 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1972 ns = NS_PREFIX_LVM + self.sr.uuid 

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

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

1975 

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

1977 # the leaf without being inherited by anyone else 

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

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

1980 del self.sm_config[key] 

1981 

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

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

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

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

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

1987 vdiRef = snapVDI2._db_introduce() 

1988 if cloneOp: 

1989 vdi_info = {UUID_TAG: snapVDI2.uuid, 

1990 NAME_LABEL_TAG: util.to_plain_string( \ 

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

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

1993 NAME_DESCRIPTION_TAG: util.to_plain_string( \ 

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

1995 IS_A_SNAPSHOT_TAG: 0, 

1996 SNAPSHOT_OF_TAG: '', 

1997 SNAPSHOT_TIME_TAG: '', 

1998 TYPE_TAG: type, 

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

2000 READ_ONLY_TAG: 0, 

2001 MANAGED_TAG: int(snapVDI2.managed), 

2002 METADATA_OF_POOL_TAG: '' 

2003 } 

2004 else: 

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

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

2007 vdi_info = {UUID_TAG: snapVDI2.uuid, 

2008 NAME_LABEL_TAG: util.to_plain_string( \ 

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

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

2011 NAME_DESCRIPTION_TAG: util.to_plain_string( \ 

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

2013 IS_A_SNAPSHOT_TAG: 1, 

2014 SNAPSHOT_OF_TAG: snapVDI.uuid, 

2015 SNAPSHOT_TIME_TAG: '', 

2016 TYPE_TAG: type, 

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

2018 READ_ONLY_TAG: 0, 

2019 MANAGED_TAG: int(snapVDI2.managed), 

2020 METADATA_OF_POOL_TAG: '' 

2021 } 

2022 

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

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

2025 (vdiRef, snapVDI2.uuid)) 

2026 

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

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

2029 vdiRef = self._db_introduce() 

2030 vdi_info = {UUID_TAG: self.uuid, 

2031 NAME_LABEL_TAG: self.label, 

2032 NAME_DESCRIPTION_TAG: self.description, 

2033 IS_A_SNAPSHOT_TAG: 0, 

2034 SNAPSHOT_OF_TAG: '', 

2035 SNAPSHOT_TIME_TAG: '', 

2036 TYPE_TAG: type, 

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

2038 READ_ONLY_TAG: 1, 

2039 MANAGED_TAG: 0, 

2040 METADATA_OF_POOL_TAG: '' 

2041 } 

2042 

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

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

2045 (vdiRef, self.uuid)) 

2046 

2047 # Update the original record 

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

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

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

2051 str(snapVDI.utilisation)) 

2052 

2053 # Return the info on the new snap VDI 

2054 snap = snapVDI2 

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

2056 snap = self 

2057 if not basePresent: 

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

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

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

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

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

2063 snap = snapVDI 

2064 return snap.get_params() 

2065 

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

2067 self.vdi_type = vdiType 

2068 self.cowutil = getCowUtil(self.vdi_type) 

2069 self.lvmcowutil = LvmCowUtil(self.cowutil) 

2070 

2071 def _initFromVDIInfo(self, vdiInfo): 

2072 self._setType(vdiInfo.vdiType) 

2073 self.lvname = vdiInfo.lvName 

2074 self.size = vdiInfo.sizeVirt 

2075 self.utilisation = vdiInfo.sizeLV 

2076 self.hidden = vdiInfo.hidden 

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

2078 self.managed = False 

2079 self.active = vdiInfo.lvActive 

2080 self.readonly = vdiInfo.lvReadonly 

2081 self.parent = vdiInfo.parentUuid 

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

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

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

2085 else: 

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

2087 self.loaded = True 

2088 

2089 def _initFromLVInfo(self, lvInfo): 

2090 self._setType(lvInfo.vdiType) 

2091 self.lvname = lvInfo.name 

2092 self.size = lvInfo.size 

2093 self.utilisation = lvInfo.size 

2094 self.hidden = lvInfo.hidden 

2095 self.active = lvInfo.active 

2096 self.readonly = lvInfo.readonly 

2097 self.parent = '' 

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

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

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

2101 else: 

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

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

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

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

2106 self.loaded = True 

2107 

2108 def _initFromImageInfo(self, imageInfo): 

2109 self.size = imageInfo.sizeVirt 

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

2111 self.parent = imageInfo.parentUuid 

2112 self.hidden = imageInfo.hidden 

2113 self.loaded = True 

2114 

2115 def _determineType(self): 

2116 """ 

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

2118 """ 

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

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

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

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

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

2124 prefix = LV_PREFIX[self.vdi_type] 

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

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

2127 self.sm_config_override = sm_config 

2128 return True 

2129 

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

2131 # the LV is active 

2132 found = False 

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

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

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

2136 if util.pathexists(path): 

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

2138 raise xs_errors.XenError('VDILoad', 

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

2140 found = True 

2141 self._setType(vdi_type) 

2142 self.lvname = lvname 

2143 self.path = path 

2144 if found: 

2145 return True 

2146 

2147 # now list all LV's 

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

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

2150 return False 

2151 

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

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

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

2155 return True 

2156 return False 

2157 

2158 def _loadThis(self): 

2159 """ 

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

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

2162 """ 

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

2164 if VdiType.isCowImage(self.vdi_type): 

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

2166 return 

2167 try: 

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

2169 except util.CommandException as e: 

2170 raise xs_errors.XenError('VDIUnavailable', 

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

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

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

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

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

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

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

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

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

2180 self._initFromImageInfo(imageInfo) 

2181 self.loaded = True 

2182 

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

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

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

2186 NS_PREFIX_LVM + self.sr.uuid) 

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

2188 return # this is a redundant activation/deactivation call 

2189 

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

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

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

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

2194 binaryParam = binary 

2195 if uuid != self.uuid: 

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

2197 if active: 

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

2199 persistent) 

2200 else: 

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

2202 # step. The LVs must not have been activated during the current 

2203 # operation 

2204 self.sr.lvActivator.add(uuid, lvName, binaryParam) 

2205 

2206 def _failClone(self, uuid, jval, msg): 

2207 try: 

2208 self.sr._handleInterruptedCloneOp(uuid, jval, True) 

2209 self.sr.journaler.remove(self.JRN_CLONE, uuid) 

2210 except Exception as e: 

2211 util.SMlog('WARNING: failed to clean up failed snapshot: ' \ 

2212 ' %s (error ignored)' % e) 

2213 raise xs_errors.XenError('VDIClone', opterr=msg) 

2214 

2215 def _markHidden(self): 

2216 if not VdiType.isCowImage(self.vdi_type): 

2217 self.sr.lvmCache.setHidden(self.lvname) 

2218 else: 

2219 self.cowutil.setHidden(self.path) 

2220 self.hidden = 1 

2221 

2222 def _prepareThin(self, attach, vdiType): 

2223 origUtilisation = self.sr.lvmCache.getSize(self.lvname) 

2224 if self.sr.isMaster: 

2225 # the master can prepare the VDI locally 

2226 if attach: 

2227 self.lvmcowutil.attachThin(self.sr.journaler, self.sr.uuid, self.uuid, self.vdi_type) 

2228 else: 

2229 self.lvmcowutil.detachThin(self.session, self.sr.lvmCache, self.sr.uuid, self.uuid, self.vdi_type) 

2230 else: 

2231 fn = "attach" 

2232 if not attach: 

2233 fn = "detach" 

2234 pools = self.session.xenapi.pool.get_all() 

2235 master = self.session.xenapi.pool.get_master(pools[0]) 

2236 rv = self.session.xenapi.host.call_plugin( 

2237 master, 

2238 self.sr.THIN_PLUGIN, 

2239 fn, 

2240 { 

2241 "srUuid": self.sr.uuid, 

2242 "vdiUuid": self.uuid, 

2243 "vdiType": vdiType 

2244 } 

2245 ) 

2246 util.SMlog("call-plugin returned: %s" % rv) 

2247 if not rv: 

2248 raise Exception('plugin %s failed' % self.sr.THIN_PLUGIN) 

2249 # refresh to pick up the size change on this slave 

2250 self.sr.lvmCache.activateNoRefcount(self.lvname, True) 

2251 

2252 self.utilisation = self.sr.lvmCache.getSize(self.lvname) 

2253 if origUtilisation != self.utilisation: 

2254 vdi_ref = self.sr.srcmd.params['vdi_ref'] 

2255 self.session.xenapi.VDI.set_physical_utilisation(vdi_ref, 

2256 str(self.utilisation)) 

2257 stats = lvutil._getVGstats(self.sr.vgname) 

2258 sr_utilisation = stats['physical_utilisation'] 

2259 self.session.xenapi.SR.set_physical_utilisation(self.sr.sr_ref, 

2260 str(sr_utilisation)) 

2261 

2262 @override 

2263 def update(self, sr_uuid, vdi_uuid) -> None: 

2264 if self.sr.legacyMode: 

2265 return 

2266 

2267 #Synch the name_label of this VDI on storage with the name_label in XAPI 

2268 vdi_ref = self.session.xenapi.VDI.get_by_uuid(self.uuid) 

2269 update_map = {} 

2270 update_map[METADATA_UPDATE_OBJECT_TYPE_TAG] = \ 

2271 METADATA_OBJECT_TYPE_VDI 

2272 update_map[UUID_TAG] = self.uuid 

2273 update_map[NAME_LABEL_TAG] = util.to_plain_string( \ 

2274 self.session.xenapi.VDI.get_name_label(vdi_ref)) 

2275 update_map[NAME_DESCRIPTION_TAG] = util.to_plain_string( \ 

2276 self.session.xenapi.VDI.get_name_description(vdi_ref)) 

2277 update_map[SNAPSHOT_TIME_TAG] = \ 

2278 self.session.xenapi.VDI.get_snapshot_time(vdi_ref) 

2279 update_map[METADATA_OF_POOL_TAG] = \ 

2280 self.session.xenapi.VDI.get_metadata_of_pool(vdi_ref) 

2281 LVMMetadataHandler(self.sr.mdpath).updateMetadata(update_map) 

2282 

2283 @override 

2284 def _ensure_cbt_space(self) -> None: 

2285 # We need virtual_size to compute the size in case of a bigger VDI 

2286 self.sr.ensureCBTSpace(self.size) 

2287 

2288 @override 

2289 def _create_cbt_log(self) -> str: 

2290 logname = self._get_cbt_logname(self.uuid) 

2291 logsize = max(util.roundup(CBT_BLOCK_SIZE, self.size//CBT_BLOCK_SIZE), self.sr.journaler.LV_SIZE) 

2292 # We choose 4MiB as the minimum for the log size to maintain the old behavior and compute the correct amount 

2293 # if we need a bigger LV for the CBT (can happen with big QCOW2) 

2294 self.sr.lvmCache.create(logname, logsize, CBTLOG_TAG) 

2295 logpath = super(LVMVDI, self)._create_cbt_log() 

2296 self.sr.lvmCache.deactivateNoRefcount(logname) 

2297 return logpath 

2298 

2299 @override 

2300 def _delete_cbt_log(self) -> None: 

2301 logpath = self._get_cbt_logpath(self.uuid) 

2302 if self._cbt_log_exists(logpath): 

2303 logname = self._get_cbt_logname(self.uuid) 

2304 self.sr.lvmCache.remove(logname) 

2305 

2306 @override 

2307 def _rename(self, oldpath, newpath) -> None: 

2308 oldname = os.path.basename(oldpath) 

2309 newname = os.path.basename(newpath) 

2310 self.sr.lvmCache.rename(oldname, newname) 

2311 

2312 @override 

2313 def update_slaves_on_cbt_disable(self, cbtlog) -> None: 

2314 args = { 

2315 "vgName": self.sr.vgname, 

2316 "action1": "deactivateNoRefcount", 

2317 "lvName1": cbtlog 

2318 } 

2319 

2320 host_refs = util.get_hosts_attached_on(self.session, [self.uuid]) 

2321 

2322 message = f"Deactivating {cbtlog}" 

2323 self.sr.call_on_slave(args, host_refs, message) 

2324 

2325 @override 

2326 def _activate_cbt_log(self, lv_name) -> bool: 

2327 self.sr.lvmCache.refresh() 

2328 if not self.sr.lvmCache.is_active(lv_name): 2328 ↛ 2329line 2328 didn't jump to line 2329, because the condition on line 2328 was never true

2329 try: 

2330 self.sr.lvmCache.activateNoRefcount(lv_name) 

2331 return True 

2332 except Exception as e: 

2333 util.SMlog("Exception in _activate_cbt_log, " 

2334 "Error: %s." % str(e)) 

2335 raise 

2336 else: 

2337 return False 

2338 

2339 @override 

2340 def _deactivate_cbt_log(self, lv_name) -> None: 

2341 try: 

2342 self.sr.lvmCache.deactivateNoRefcount(lv_name) 

2343 except Exception as e: 

2344 util.SMlog("Exception in _deactivate_cbt_log, Error: %s." % str(e)) 

2345 raise 

2346 

2347 @override 

2348 def _cbt_log_exists(self, logpath) -> bool: 

2349 return lvutil.exists(logpath) 

2350 

2351if __name__ == '__main__': 2351 ↛ 2352line 2351 didn't jump to line 2352, because the condition on line 2351 was never true

2352 SRCommand.run(LVMSR, DRIVER_INFO) 

2353else: 

2354 SR.registerSR(LVMSR)