Coverage for libs/sm/drivers/FileSR.py: 52%

767 statements  

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

1# Copyright (C) Citrix Systems Inc. 

2# 

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

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

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

6# 

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

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

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

10# GNU Lesser General Public License for more details. 

11# 

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

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

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

15# 

16# FileSR: local-file storage repository 

17# matches with drivers/FileSR 

18 

19import os 

20import errno 

21import time 

22import glob 

23from uuid import uuid4 

24import xmlrpc.client 

25 

26import XenAPI # pylint: disable=import-error 

27 

28from sm import SR 

29from sm import VDI 

30from sm import vhdutil 

31from sm import cleanup 

32from sm import blktap2 

33from sm.core import util 

34from sm.core import scsiutil 

35from sm.core import xs_errors 

36from sm.core.lock import Lock 

37from sm.constants import CBTLOG_TAG 

38 

39geneology = {} 

40CAPABILITIES = ["SR_PROBE", "SR_UPDATE", \ 

41 "VDI_CREATE", "VDI_DELETE", "VDI_ATTACH", "VDI_DETACH", \ 

42 "VDI_CLONE", "VDI_SNAPSHOT", "VDI_RESIZE", "VDI_MIRROR", 

43 "VDI_GENERATE_CONFIG", "ATOMIC_PAUSE", "VDI_CONFIG_CBT", 

44 "VDI_ACTIVATE", "VDI_DEACTIVATE", "THIN_PROVISIONING"] 

45 

46CONFIGURATION = [['location', 'local directory path (required)']] 

47 

48DRIVER_INFO = { 

49 'name': 'Local Path VHD', 

50 'description': 'SR plugin which represents disks as VHD files stored on a local path', 

51 'vendor': 'Citrix Systems Inc', 

52 'copyright': '(C) 2008 Citrix Systems Inc', 

53 'driver_version': '1.0', 

54 'required_api_version': '1.0', 

55 'capabilities': CAPABILITIES, 

56 'configuration': CONFIGURATION 

57 } 

58 

59JOURNAL_FILE_PREFIX = ".journal-" 

60 

61OPS_EXCLUSIVE = [ 

62 "sr_create", "sr_delete", "sr_probe", "sr_attach", "sr_detach", 

63 "sr_scan", "vdi_init", "vdi_create", "vdi_delete", "vdi_attach", 

64 "vdi_detach", "vdi_resize_online", "vdi_snapshot", "vdi_clone"] 

65 

66DRIVER_CONFIG = {"ATTACH_FROM_CONFIG_WITH_TAPDISK": True} 

67 

68 

69class FileSR(SR.SR): 

70 """Local file storage repository""" 

71 

72 SR_TYPE = "file" 

73 

74 def handles(srtype): 

75 return srtype == 'file' 

76 handles = staticmethod(handles) 

77 

78 def _check_o_direct(self): 

79 if self.sr_ref and self.session is not None: 

80 other_config = self.session.xenapi.SR.get_other_config(self.sr_ref) 

81 o_direct = other_config.get("o_direct") 

82 self.o_direct = o_direct is not None and o_direct == "true" 

83 else: 

84 self.o_direct = True 

85 

86 def __init__(self, srcmd, sr_uuid): 

87 # We call SR.SR.__init__ explicitly because 

88 # "super" sometimes failed due to circular imports 

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

90 self._check_o_direct() 

91 

92 def load(self, sr_uuid): 

93 self.ops_exclusive = OPS_EXCLUSIVE 

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

95 self.sr_vditype = vhdutil.VDI_TYPE_VHD 

96 if 'location' not in self.dconf or not self.dconf['location']: 

97 raise xs_errors.XenError('ConfigLocationMissing') 

98 self.remotepath = self.dconf['location'] 

99 self.path = os.path.join(SR.MOUNT_BASE, sr_uuid) 

100 self.linkpath = self.path 

101 self.mountpoint = self.path 

102 self.attached = False 

103 self.driver_config = DRIVER_CONFIG 

104 

105 def create(self, sr_uuid, size): 

106 """ Create the SR. The path must not already exist, or if it does,  

107 it must be empty. (This accounts for the case where the user has 

108 mounted a device onto a directory manually and want to use this as the 

109 root of a file-based SR.) """ 

110 try: 

111 if util.ioretry(lambda: util.pathexists(self.remotepath)): 

112 if len(util.ioretry(lambda: util.listdir(self.remotepath))) != 0: 

113 raise xs_errors.XenError('SRExists') 

114 else: 

115 try: 

116 util.ioretry(lambda: os.mkdir(self.remotepath)) 

117 except util.CommandException as inst: 

118 if inst.code == errno.EEXIST: 

119 raise xs_errors.XenError('SRExists') 

120 else: 

121 raise xs_errors.XenError('FileSRCreate', \ 

122 opterr='directory creation failure %d' \ 

123 % inst.code) 

124 except: 

125 raise xs_errors.XenError('FileSRCreate') 

126 

127 def delete(self, sr_uuid): 

128 self.attach(sr_uuid) 

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

130 

131 # check to make sure no VDIs are present; then remove old 

132 # files that are non VDI's 

133 try: 

134 if util.ioretry(lambda: util.pathexists(self.path)): 

135 #Load the VDI list 

136 self._loadvdis() 

137 for uuid in self.vdis: 

138 if not self.vdis[uuid].deleted: 

139 raise xs_errors.XenError('SRNotEmpty', \ 

140 opterr='VDIs still exist in SR') 

141 

142 # remove everything else, there are no vdi's 

143 for name in util.ioretry(lambda: util.listdir(self.path)): 

144 fullpath = os.path.join(self.path, name) 

145 try: 

146 util.ioretry(lambda: os.unlink(fullpath)) 

147 except util.CommandException as inst: 

148 if inst.code != errno.ENOENT and \ 

149 inst.code != errno.EISDIR: 

150 raise xs_errors.XenError('FileSRDelete', \ 

151 opterr='failed to remove %s error %d' \ 

152 % (fullpath, inst.code)) 

153 self.detach(sr_uuid) 

154 except util.CommandException as inst: 

155 self.detach(sr_uuid) 

156 raise xs_errors.XenError('FileSRDelete', \ 

157 opterr='error %d' % inst.code) 

158 

159 def attach(self, sr_uuid, bind=True): 

160 if not self._checkmount(): 

161 try: 

162 util.ioretry(lambda: util.makedirs(self.path, mode=0o700)) 

163 except util.CommandException as inst: 

164 if inst.code != errno.EEXIST: 

165 raise xs_errors.XenError("FileSRCreate", \ 

166 opterr='fail to create mount point. Errno is %s' % inst.code) 

167 try: 

168 cmd = ["mount", self.remotepath, self.path] 

169 if bind: 

170 cmd.append("--bind") 

171 util.pread(cmd) 

172 os.chmod(self.path, mode=0o0700) 

173 except util.CommandException as inst: 

174 raise xs_errors.XenError('FileSRCreate', \ 

175 opterr='fail to mount FileSR. Errno is %s' % inst.code) 

176 self.attached = True 

177 

178 def detach(self, sr_uuid): 

179 if self._checkmount(): 

180 try: 

181 util.SMlog("Aborting GC/coalesce") 

182 cleanup.abort(self.uuid) 

183 os.chdir(SR.MOUNT_BASE) 

184 util.pread(["umount", self.path]) 

185 os.rmdir(self.path) 

186 except Exception as e: 

187 raise xs_errors.XenError('SRInUse', opterr=str(e)) 

188 self.attached = False 

189 

190 def scan(self, sr_uuid): 

191 if not self._checkmount(): 191 ↛ 192line 191 didn't jump to line 192, because the condition on line 191 was never true

192 raise xs_errors.XenError('SRUnavailable', \ 

193 opterr='no such directory %s' % self.path) 

194 

195 if not self.vdis: 195 ↛ 198line 195 didn't jump to line 198, because the condition on line 195 was never false

196 self._loadvdis() 

197 

198 if not self.passthrough: 198 ↛ 202line 198 didn't jump to line 202, because the condition on line 198 was never false

199 self.physical_size = self._getsize() 

200 self.physical_utilisation = self._getutilisation() 

201 

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

203 if self.vdis[uuid].deleted: 203 ↛ 204line 203 didn't jump to line 204, because the condition on line 203 was never true

204 del self.vdis[uuid] 

205 

206 # CA-15607: make sure we are robust to the directory being unmounted beneath 

207 # us (eg by a confused user). Without this we might forget all our VDI references 

208 # which would be a shame. 

209 # For SMB SRs, this path is mountpoint 

210 mount_path = self.path 

211 if self.handles("smb"): 211 ↛ 212line 211 didn't jump to line 212, because the condition on line 211 was never true

212 mount_path = self.mountpoint 

213 

214 if not self.handles("file") and not os.path.ismount(mount_path): 214 ↛ 215line 214 didn't jump to line 215, because the condition on line 214 was never true

215 util.SMlog("Error: FileSR.scan called but directory %s isn't a mountpoint" % mount_path) 

216 raise xs_errors.XenError('SRUnavailable', \ 

217 opterr='not mounted %s' % mount_path) 

218 

219 self._kickGC() 

220 

221 # default behaviour from here on 

222 super(FileSR, self).scan(sr_uuid) 

223 

224 def update(self, sr_uuid): 

225 if not self._checkmount(): 

226 raise xs_errors.XenError('SRUnavailable', \ 

227 opterr='no such directory %s' % self.path) 

228 self._update(sr_uuid, 0) 

229 

230 def _update(self, sr_uuid, virt_alloc_delta): 

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

232 self.virtual_allocation = valloc + virt_alloc_delta 

233 self.physical_size = self._getsize() 

234 self.physical_utilisation = self._getutilisation() 

235 self._db_update() 

236 

237 def content_type(self, sr_uuid): 

238 return super(FileSR, self).content_type(sr_uuid) 

239 

240 def vdi(self, uuid): 

241 return FileVDI(self, uuid) 

242 

243 def added_vdi(self, vdi): 

244 self.vdis[vdi.uuid] = vdi 

245 

246 def deleted_vdi(self, uuid): 

247 if uuid in self.vdis: 

248 del self.vdis[uuid] 

249 

250 def replay(self, uuid): 

251 try: 

252 file = open(self.path + "/filelog.txt", "r") 

253 data = file.readlines() 

254 file.close() 

255 self._process_replay(data) 

256 except: 

257 raise xs_errors.XenError('SRLog') 

258 

259 def _loadvdis(self): 

260 if self.vdis: 260 ↛ 261line 260 didn't jump to line 261, because the condition on line 260 was never true

261 return 

262 

263 pattern = os.path.join(self.path, "*%s" % vhdutil.FILE_EXTN_VHD) 

264 try: 

265 self.vhds = vhdutil.getAllVHDs(pattern, FileVDI.extractUuid) 

266 except util.CommandException as inst: 

267 raise xs_errors.XenError('SRScan', opterr="error VHD-scanning " \ 

268 "path %s (%s)" % (self.path, inst)) 

269 try: 

270 list_vhds = [FileVDI.extractUuid(v) for v in util.ioretry(lambda: glob.glob(pattern))] 270 ↛ exitline 270 didn't run the lambda on line 270

271 if len(self.vhds) != len(list_vhds): 271 ↛ 276line 271 didn't jump to line 276, because the condition on line 271 was never false

272 util.SMlog("VHD scan returns %d VHDs: %s" % (len(self.vhds), sorted(self.vhds))) 

273 util.SMlog("VHD list returns %d VHDs: %s" % (len(list_vhds), sorted(list_vhds))) 

274 except: 

275 pass 

276 for uuid in self.vhds.keys(): 

277 if self.vhds[uuid].error: 277 ↛ 278line 277 didn't jump to line 278, because the condition on line 277 was never true

278 raise xs_errors.XenError('SRScan', opterr='uuid=%s' % uuid) 

279 self.vdis[uuid] = self.vdi(uuid) 

280 # Get the key hash of any encrypted VDIs: 

281 vhd_path = os.path.join(self.path, self.vhds[uuid].path) 

282 key_hash = vhdutil.getKeyHash(vhd_path) 

283 self.vdis[uuid].sm_config_override['key_hash'] = key_hash 

284 

285 # raw VDIs and CBT log files 

286 files = util.ioretry(lambda: util.listdir(self.path)) 286 ↛ exitline 286 didn't run the lambda on line 286

287 for fn in files: 287 ↛ 288line 287 didn't jump to line 288, because the loop on line 287 never started

288 if fn.endswith(vhdutil.FILE_EXTN_RAW): 

289 uuid = fn[:-(len(vhdutil.FILE_EXTN_RAW))] 

290 self.vdis[uuid] = self.vdi(uuid) 

291 elif fn.endswith(CBTLOG_TAG): 

292 cbt_uuid = fn.split(".")[0] 

293 # If an associated disk exists, update CBT status 

294 # else create new VDI of type cbt_metadata 

295 if cbt_uuid in self.vdis: 

296 self.vdis[cbt_uuid].cbt_enabled = True 

297 else: 

298 new_vdi = self.vdi(cbt_uuid) 

299 new_vdi.ty = "cbt_metadata" 

300 new_vdi.cbt_enabled = True 

301 self.vdis[cbt_uuid] = new_vdi 

302 

303 # Mark parent VDIs as Read-only and generate virtual allocation 

304 self.virtual_allocation = 0 

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

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

307 if vdi.parent in self.vdis: 

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

309 if vdi.parent in geneology: 

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

311 else: 

312 geneology[vdi.parent] = [uuid] 

313 if not vdi.hidden: 313 ↛ 305line 313 didn't jump to line 305, because the condition on line 313 was never false

314 self.virtual_allocation += (vdi.size) 

315 

316 # now remove all hidden leaf nodes from self.vdis so that they are not 

317 # introduced into the Agent DB when SR is synchronized. With the 

318 # asynchronous GC, a deleted VDI might stay around until the next 

319 # SR.scan, so if we don't ignore hidden leaves we would pick up 

320 # freshly-deleted VDIs as newly-added VDIs 

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

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

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

324 del self.vdis[uuid] 

325 

326 def _getsize(self): 

327 path = self.path 

328 if self.handles("smb"): 328 ↛ 329line 328 didn't jump to line 329, because the condition on line 328 was never true

329 path = self.linkpath 

330 return util.get_fs_size(path) 

331 

332 def _getutilisation(self): 

333 return util.get_fs_utilisation(self.path) 

334 

335 def _replay(self, logentry): 

336 # all replay commands have the same 5,6,7th arguments 

337 # vdi_command, sr-uuid, vdi-uuid 

338 back_cmd = logentry[5].replace("vdi_", "") 

339 target = self.vdi(logentry[7]) 

340 cmd = getattr(target, back_cmd) 

341 args = [] 

342 for item in logentry[6:]: 

343 item = item.replace("\n", "") 

344 args.append(item) 

345 ret = cmd( * args) 

346 if ret: 

347 print(ret) 

348 

349 def _compare_args(self, a, b): 

350 try: 

351 if a[2] != "log:": 

352 return 1 

353 if b[2] != "end:" and b[2] != "error:": 

354 return 1 

355 if a[3] != b[3]: 

356 return 1 

357 if a[4] != b[4]: 

358 return 1 

359 return 0 

360 except: 

361 return 1 

362 

363 def _process_replay(self, data): 

364 logentries = [] 

365 for logentry in data: 

366 logentry = logentry.split(" ") 

367 logentries.append(logentry) 

368 # we are looking for a log entry that has a log but no end or error 

369 # wkcfix -- recreate (adjusted) logfile 

370 index = 0 

371 while index < len(logentries) - 1: 

372 if self._compare_args(logentries[index], logentries[index + 1]): 

373 self._replay(logentries[index]) 

374 else: 

375 # skip the paired one 

376 index += 1 

377 # next 

378 index += 1 

379 

380 def _kickGC(self): 

381 util.SMlog("Kicking GC") 

382 cleanup.start_gc_service(self.uuid) 

383 

384 def _isbind(self): 

385 # os.path.ismount can't deal with bind mount 

386 st1 = os.stat(self.path) 

387 st2 = os.stat(self.remotepath) 

388 return st1.st_dev == st2.st_dev and st1.st_ino == st2.st_ino 

389 

390 def _checkmount(self): 

391 mount_path = self.path 

392 if self.handles("smb"): 392 ↛ 393line 392 didn't jump to line 393, because the condition on line 392 was never true

393 mount_path = self.mountpoint 

394 

395 return util.ioretry(lambda: util.pathexists(mount_path) and \ 

396 (util.ismount(mount_path) or \ 

397 util.pathexists(self.remotepath) and self._isbind())) 

398 

399 # Override in SharedFileSR. 

400 def _check_hardlinks(self): 

401 return True 

402 

403class FileVDI(VDI.VDI): 

404 PARAM_VHD = "vhd" 

405 PARAM_RAW = "raw" 

406 VDI_TYPE = { 

407 PARAM_VHD: vhdutil.VDI_TYPE_VHD, 

408 PARAM_RAW: vhdutil.VDI_TYPE_RAW 

409 } 

410 

411 def _find_path_with_retries(self, vdi_uuid, maxretry=5, period=2.0): 

412 vhd_path = os.path.join(self.sr.path, "%s.%s" % \ 

413 (vdi_uuid, self.PARAM_VHD)) 

414 raw_path = os.path.join(self.sr.path, "%s.%s" % \ 

415 (vdi_uuid, self.PARAM_RAW)) 

416 cbt_path = os.path.join(self.sr.path, "%s.%s" % 

417 (vdi_uuid, CBTLOG_TAG)) 

418 found = False 

419 tries = 0 

420 while tries < maxretry and not found: 

421 tries += 1 

422 if util.ioretry(lambda: util.pathexists(vhd_path)): 

423 self.vdi_type = vhdutil.VDI_TYPE_VHD 

424 self.path = vhd_path 

425 found = True 

426 elif util.ioretry(lambda: util.pathexists(raw_path)): 

427 self.vdi_type = vhdutil.VDI_TYPE_RAW 

428 self.path = raw_path 

429 self.hidden = False 

430 found = True 

431 elif util.ioretry(lambda: util.pathexists(cbt_path)): 431 ↛ 432line 431 didn't jump to line 432, because the condition on line 431 was never true

432 self.vdi_type = CBTLOG_TAG 

433 self.path = cbt_path 

434 self.hidden = False 

435 found = True 

436 

437 if not found: 

438 util.SMlog("VHD %s not found, retry %s of %s" % (vhd_path, tries, maxretry)) 

439 time.sleep(period) 

440 

441 return found 

442 

443 def load(self, vdi_uuid): 

444 self.lock = self.sr.lock 

445 

446 self.sr.srcmd.params['o_direct'] = self.sr.o_direct 

447 

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

449 self.vdi_type = vhdutil.VDI_TYPE_VHD 

450 self.key_hash = None 

451 if "vdi_sm_config" in self.sr.srcmd.params: 451 ↛ 452line 451 didn't jump to line 452, because the condition on line 451 was never true

452 if "key_hash" in self.sr.srcmd.params["vdi_sm_config"]: 

453 self.key_hash = self.sr.srcmd.params["vdi_sm_config"]["key_hash"] 

454 

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

456 vdi_type = self.sr.srcmd.params["vdi_sm_config"]["type"] 

457 if not self.VDI_TYPE.get(vdi_type): 

458 raise xs_errors.XenError('VDIType', 

459 opterr='Invalid VDI type %s' % vdi_type) 

460 self.vdi_type = self.VDI_TYPE[vdi_type] 

461 self.path = os.path.join(self.sr.path, "%s%s" % 

462 (vdi_uuid, vhdutil.FILE_EXTN[self.vdi_type])) 

463 else: 

464 found = self._find_path_with_retries(vdi_uuid) 

465 if not found: 465 ↛ 466line 465 didn't jump to line 466, because the condition on line 465 was never true

466 if self.sr.srcmd.cmd == "vdi_delete": 

467 # Could be delete for CBT log file 

468 self.path = os.path.join(self.sr.path, "%s.%s" % 

469 (vdi_uuid, self.PARAM_VHD)) 

470 return 

471 if self.sr.srcmd.cmd == "vdi_attach_from_config": 

472 return 

473 raise xs_errors.XenError('VDIUnavailable', 

474 opterr="VDI %s not found" % vdi_uuid) 

475 

476 

477 if self.vdi_type == vhdutil.VDI_TYPE_VHD and \ 

478 self.sr.__dict__.get("vhds") and self.sr.vhds.get(vdi_uuid): 

479 # VHD info already preloaded: use it instead of querying directly 

480 vhdInfo = self.sr.vhds[vdi_uuid] 

481 self.utilisation = vhdInfo.sizePhys 

482 self.size = vhdInfo.sizeVirt 

483 self.hidden = vhdInfo.hidden 

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

485 self.managed = False 

486 self.parent = vhdInfo.parentUuid 

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

488 self.sm_config_override = {'vhd-parent': self.parent} 

489 else: 

490 self.sm_config_override = {'vhd-parent': None} 

491 return 

492 

493 try: 

494 # Change to the SR directory in case parent 

495 # locator field path has changed 

496 os.chdir(self.sr.path) 

497 except Exception as chdir_exception: 

498 util.SMlog("Unable to change to SR directory, SR unavailable, %s" % 

499 str(chdir_exception)) 

500 raise xs_errors.XenError('SRUnavailable', opterr=str(chdir_exception)) 

501 

502 if util.ioretry( 502 ↛ exitline 502 didn't return from function 'load', because the condition on line 502 was never false

503 lambda: util.pathexists(self.path), 

504 errlist=[errno.EIO, errno.ENOENT]): 

505 try: 

506 st = util.ioretry(lambda: os.stat(self.path), 

507 errlist=[errno.EIO, errno.ENOENT]) 

508 self.utilisation = int(st.st_size) 

509 except util.CommandException as inst: 

510 if inst.code == errno.EIO: 

511 raise xs_errors.XenError('VDILoad', \ 

512 opterr='Failed load VDI information %s' % self.path) 

513 else: 

514 util.SMlog("Stat failed for %s, %s" % ( 

515 self.path, str(inst))) 

516 raise xs_errors.XenError('VDIType', \ 

517 opterr='Invalid VDI type %s' % self.vdi_type) 

518 

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

520 self.exists = True 

521 self.size = self.utilisation 

522 self.sm_config_override = {'type': self.PARAM_RAW} 

523 return 

524 

525 if self.vdi_type == CBTLOG_TAG: 525 ↛ 526line 525 didn't jump to line 526, because the condition on line 525 was never true

526 self.exists = True 

527 self.size = self.utilisation 

528 return 

529 

530 try: 

531 # The VDI might be activated in R/W mode so the VHD footer 

532 # won't be valid, use the back-up one instead. 

533 diskinfo = util.ioretry( 

534 lambda: self._query_info(self.path, True), 

535 errlist=[errno.EIO, errno.ENOENT]) 

536 

537 if 'parent' in diskinfo: 537 ↛ 538line 537 didn't jump to line 538, because the condition on line 537 was never true

538 self.parent = diskinfo['parent'] 

539 self.sm_config_override = {'vhd-parent': self.parent} 

540 else: 

541 self.sm_config_override = {'vhd-parent': None} 

542 self.parent = '' 

543 self.size = int(diskinfo['size']) * 1024 * 1024 

544 self.hidden = int(diskinfo['hidden']) 

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

546 self.managed = False 

547 self.exists = True 

548 except util.CommandException as inst: 

549 raise xs_errors.XenError('VDILoad', \ 

550 opterr='Failed load VDI information %s' % self.path) 

551 

552 def update(self, sr_uuid, vdi_location): 

553 self.load(vdi_location) 

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

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

556 self._db_update() 

557 

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

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

560 raise xs_errors.XenError('VDIExists') 

561 

562 if self.vdi_type == vhdutil.VDI_TYPE_VHD: 

563 try: 

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

565 mb = 1024 * 1024 

566 size_mb = size // mb 

567 util.ioretry(lambda: self._create(str(size_mb), self.path)) 

568 self.size = util.ioretry(lambda: self._query_v(self.path)) 

569 except util.CommandException as inst: 

570 raise xs_errors.XenError('VDICreate', 

571 opterr='error %d' % inst.code) 

572 else: 

573 f = open(self.path, 'w') 

574 f.truncate(int(size)) 

575 f.close() 

576 self.size = size 

577 

578 self.sr.added_vdi(self) 

579 

580 st = util.ioretry(lambda: os.stat(self.path)) 

581 self.utilisation = int(st.st_size) 

582 if self.vdi_type == vhdutil.VDI_TYPE_RAW: 

583 self.sm_config = {"type": self.PARAM_RAW} 

584 

585 self._db_introduce() 

586 self.sr._update(self.sr.uuid, self.size) 

587 return super(FileVDI, self).get_params() 

588 

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

590 if not util.ioretry(lambda: util.pathexists(self.path)): 

591 return super(FileVDI, self).delete(sr_uuid, vdi_uuid, data_only) 

592 

593 if self.attached: 

594 raise xs_errors.XenError('VDIInUse') 

595 

596 try: 

597 util.force_unlink(self.path) 

598 except Exception as e: 

599 raise xs_errors.XenError( 

600 'VDIDelete', 

601 opterr='Failed to unlink file during deleting VDI: %s' % str(e)) 

602 

603 self.sr.deleted_vdi(vdi_uuid) 

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

605 if not data_only: 

606 self._db_forget() 

607 self.sr._update(self.sr.uuid, -self.size) 

608 self.sr.lock.cleanupAll(vdi_uuid) 

609 self.sr._kickGC() 

610 return super(FileVDI, self).delete(sr_uuid, vdi_uuid, data_only) 

611 

612 def attach(self, sr_uuid, vdi_uuid): 

613 if self.path is None: 

614 self._find_path_with_retries(vdi_uuid) 

615 if not self._checkpath(self.path): 

616 raise xs_errors.XenError('VDIUnavailable', \ 

617 opterr='VDI %s unavailable %s' % (vdi_uuid, self.path)) 

618 try: 

619 self.attached = True 

620 

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

622 self.xenstore_data = {} 

623 

624 self.xenstore_data.update(scsiutil.update_XS_SCSIdata(vdi_uuid, \ 

625 scsiutil.gen_synthetic_page_data(vdi_uuid))) 

626 

627 if self.sr.handles("file"): 

628 # XXX: PR-1255: if these are constants then they should 

629 # be returned by the attach API call, not persisted in the 

630 # pool database. 

631 self.xenstore_data['storage-type'] = 'ext' 

632 return super(FileVDI, self).attach(sr_uuid, vdi_uuid) 

633 except util.CommandException as inst: 

634 raise xs_errors.XenError('VDILoad', opterr='error %d' % inst.code) 

635 

636 def detach(self, sr_uuid, vdi_uuid): 

637 self.attached = False 

638 

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

640 if not self.exists: 

641 raise xs_errors.XenError('VDIUnavailable', \ 

642 opterr='VDI %s unavailable %s' % (vdi_uuid, self.path)) 

643 

644 if self.vdi_type != vhdutil.VDI_TYPE_VHD: 

645 raise xs_errors.XenError('Unimplemented') 

646 

647 if self.hidden: 

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

649 

650 if size < self.size: 

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

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

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

654 

655 if size == self.size: 

656 return VDI.VDI.get_params(self) 

657 

658 # We already checked it is a VDI_TYPE_VHD 

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

660 

661 jFile = JOURNAL_FILE_PREFIX + self.uuid 

662 try: 

663 vhdutil.setSizeVirt(self.path, size, jFile) 

664 except: 

665 # Revert the operation 

666 vhdutil.revert(self.path, jFile) 

667 raise xs_errors.XenError('VDISize', opterr='resize operation failed') 

668 

669 old_size = self.size 

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

671 st = util.ioretry(lambda: os.stat(self.path)) 

672 self.utilisation = int(st.st_size) 

673 

674 self._db_update() 

675 self.sr._update(self.sr.uuid, self.size - old_size) 

676 super(FileVDI, self).resize_cbt(self.sr.uuid, self.uuid, self.size) 

677 return VDI.VDI.get_params(self) 

678 

679 def clone(self, sr_uuid, vdi_uuid): 

680 return self._do_snapshot(sr_uuid, vdi_uuid, VDI.SNAPSHOT_DOUBLE) 

681 

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

683 if self.vdi_type != vhdutil.VDI_TYPE_VHD: 

684 raise xs_errors.XenError('Unimplemented') 

685 parent_fn = vdi1 + vhdutil.FILE_EXTN[vhdutil.VDI_TYPE_VHD] 

686 parent_path = os.path.join(self.sr.path, parent_fn) 

687 assert(util.pathexists(parent_path)) 

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

689 vhdutil.setHidden(parent_path) 

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

691 util.pread2([vhdutil.VHD_UTIL, "modify", "-p", parent_path, 

692 "-n", self.path]) 

693 # Tell tapdisk the chain has changed 

694 if not blktap2.VDI.tap_refresh(self.session, sr_uuid, vdi2): 

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

696 util.SMlog("VDI.compose: relinked %s->%s" % (vdi2, vdi1)) 

697 

698 def reset_leaf(self, sr_uuid, vdi_uuid): 

699 if self.vdi_type != vhdutil.VDI_TYPE_VHD: 

700 raise xs_errors.XenError('Unimplemented') 

701 

702 # safety check 

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

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

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

706 

707 vhdutil.killData(self.path) 

708 

709 def _do_snapshot(self, sr_uuid, vdi_uuid, snap_type, 

710 _=False, secondary=None, cbtlog=None): 

711 # If cbt enabled, save file consistency state 

712 if cbtlog is not None: 712 ↛ 713line 712 didn't jump to line 713, because the condition on line 712 was never true

713 if blktap2.VDI.tap_status(self.session, vdi_uuid): 

714 consistency_state = False 

715 else: 

716 consistency_state = True 

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

718 (consistency_state, vdi_uuid)) 

719 else: 

720 consistency_state = None 

721 

722 if self.vdi_type != vhdutil.VDI_TYPE_VHD: 722 ↛ 723line 722 didn't jump to line 723, because the condition on line 722 was never true

723 raise xs_errors.XenError('Unimplemented') 

724 

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

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

727 try: 

728 return self._snapshot(snap_type, cbtlog, consistency_state) 

729 finally: 

730 self.disable_leaf_on_secondary(vdi_uuid, secondary=secondary) 

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

732 

733 def _rename(self, src, dst): 

734 util.SMlog("FileVDI._rename %s to %s" % (src, dst)) 

735 util.ioretry(lambda: os.rename(src, dst)) 

736 

737 def _link(self, src, dst): 

738 util.SMlog("FileVDI._link %s to %s" % (src, dst)) 

739 os.link(src, dst) 

740 

741 def _unlink(self, path): 

742 util.SMlog("FileVDI._unlink %s" % (path)) 

743 os.unlink(path) 

744 

745 def _create_new_parent(self, src, newsrc): 

746 if self.sr._check_hardlinks(): 

747 self._link(src, newsrc) 

748 else: 

749 self._rename(src, newsrc) 

750 

751 def __fist_enospace(self): 

752 raise util.CommandException(28, "vhd-util snapshot", reason="No space") 

753 

754 def _snapshot(self, snap_type, cbtlog=None, cbt_consistency=None): 

755 util.SMlog("FileVDI._snapshot for %s (type %s)" % (self.uuid, snap_type)) 

756 

757 args = [] 

758 args.append("vdi_clone") 

759 args.append(self.sr.uuid) 

760 args.append(self.uuid) 

761 

762 dest = None 

763 dst = None 

764 if snap_type == VDI.SNAPSHOT_DOUBLE: 764 ↛ 769line 764 didn't jump to line 769, because the condition on line 764 was never false

765 dest = util.gen_uuid() 

766 dst = os.path.join(self.sr.path, "%s.%s" % (dest, self.vdi_type)) 

767 args.append(dest) 

768 

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

770 raise xs_errors.XenError('VDIClone', opterr='hidden VDI') 

771 

772 depth = vhdutil.getDepth(self.path) 

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

774 raise xs_errors.XenError('VDIUnavailable', \ 

775 opterr='failed to get VHD depth') 

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

777 raise xs_errors.XenError('SnapshotChainTooLong') 

778 

779 newuuid = util.gen_uuid() 

780 src = self.path 

781 newsrc = os.path.join(self.sr.path, "%s.%s" % (newuuid, self.vdi_type)) 

782 newsrcname = "%s.%s" % (newuuid, self.vdi_type) 

783 

784 if not self._checkpath(src): 784 ↛ 785line 784 didn't jump to line 785, because the condition on line 784 was never true

785 raise xs_errors.XenError('VDIUnavailable', \ 

786 opterr='VDI %s unavailable %s' % (self.uuid, src)) 

787 

788 # wkcfix: multiphase 

789 util.start_log_entry(self.sr.path, self.path, args) 

790 

791 # We assume the filehandle has been released 

792 try: 

793 self._create_new_parent(src, newsrc) 

794 

795 # Create the snapshot under a temporary name, then rename 

796 # it afterwards. This avoids a small window where it exists 

797 # but is invalid. We do not need to do this for 

798 # snap_type == VDI.SNAPSHOT_DOUBLE because dst never existed 

799 # before so nobody will try to query it. 

800 tmpsrc = "%s.%s" % (src, "new") 

801 # Fault injection site to fail the snapshot with ENOSPACE 

802 util.fistpoint.activate_custom_fn( 

803 "FileSR_fail_snap1", 

804 self.__fist_enospace) 

805 util.ioretry(lambda: self._snap(tmpsrc, newsrcname)) 

806 # SMB3 can return EACCES if we attempt to rename over the 

807 # hardlink leaf too quickly after creating it. 

808 util.ioretry(lambda: self._rename(tmpsrc, src), 

809 errlist=[errno.EIO, errno.EACCES]) 

810 if snap_type == VDI.SNAPSHOT_DOUBLE: 810 ↛ 818line 810 didn't jump to line 818, because the condition on line 810 was never false

811 # Fault injection site to fail the snapshot with ENOSPACE 

812 util.fistpoint.activate_custom_fn( 

813 "FileSR_fail_snap2", 

814 self.__fist_enospace) 

815 util.ioretry(lambda: self._snap(dst, newsrcname)) 

816 # mark the original file (in this case, its newsrc) 

817 # as hidden so that it does not show up in subsequent scans 

818 util.ioretry(lambda: self._mark_hidden(newsrc)) 

819 

820 #Verify parent locator field of both children and delete newsrc if unused 

821 introduce_parent = True 

822 try: 

823 srcparent = util.ioretry(lambda: self._query_p_uuid(src)) 

824 dstparent = None 

825 if snap_type == VDI.SNAPSHOT_DOUBLE: 825 ↛ 827line 825 didn't jump to line 827, because the condition on line 825 was never false

826 dstparent = util.ioretry(lambda: self._query_p_uuid(dst)) 

827 if srcparent != newuuid and \ 827 ↛ 831line 827 didn't jump to line 831, because the condition on line 827 was never true

828 (snap_type == VDI.SNAPSHOT_SINGLE or \ 

829 snap_type == VDI.SNAPSHOT_INTERNAL or \ 

830 dstparent != newuuid): 

831 util.ioretry(lambda: self._unlink(newsrc)) 

832 introduce_parent = False 

833 except: 

834 pass 

835 

836 # Introduce the new VDI records 

837 leaf_vdi = None 

838 if snap_type == VDI.SNAPSHOT_DOUBLE: 838 ↛ 857line 838 didn't jump to line 857, because the condition on line 838 was never false

839 leaf_vdi = VDI.VDI(self.sr, dest) # user-visible leaf VDI 

840 leaf_vdi.read_only = False 

841 leaf_vdi.location = dest 

842 leaf_vdi.size = self.size 

843 leaf_vdi.utilisation = self.utilisation 

844 leaf_vdi.sm_config = {} 

845 leaf_vdi.sm_config['vhd-parent'] = dstparent 

846 # If the parent is encrypted set the key_hash 

847 # for the new snapshot disk 

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

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

850 if "key_hash" in sm_config: 850 ↛ 851line 850 didn't jump to line 851, because the condition on line 850 was never true

851 leaf_vdi.sm_config['key_hash'] = sm_config['key_hash'] 

852 # If we have CBT enabled on the VDI, 

853 # set CBT status for the new snapshot disk 

854 if cbtlog: 854 ↛ 855line 854 didn't jump to line 855, because the condition on line 854 was never true

855 leaf_vdi.cbt_enabled = True 

856 

857 base_vdi = None 

858 if introduce_parent: 858 ↛ 870line 858 didn't jump to line 870, because the condition on line 858 was never false

859 base_vdi = VDI.VDI(self.sr, newuuid) # readonly parent 

860 base_vdi.label = "base copy" 

861 base_vdi.read_only = True 

862 base_vdi.location = newuuid 

863 base_vdi.size = self.size 

864 base_vdi.utilisation = self.utilisation 

865 base_vdi.sm_config = {} 

866 grandparent = util.ioretry(lambda: self._query_p_uuid(newsrc)) 

867 if grandparent.find("no parent") == -1: 867 ↛ 870line 867 didn't jump to line 870, because the condition on line 867 was never false

868 base_vdi.sm_config['vhd-parent'] = grandparent 

869 

870 try: 

871 if snap_type == VDI.SNAPSHOT_DOUBLE: 871 ↛ 876line 871 didn't jump to line 876, because the condition on line 871 was never false

872 leaf_vdi_ref = leaf_vdi._db_introduce() 

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

874 (leaf_vdi_ref, dest)) 

875 

876 if introduce_parent: 876 ↛ 880line 876 didn't jump to line 880, because the condition on line 876 was never false

877 base_vdi_ref = base_vdi._db_introduce() 

878 self.session.xenapi.VDI.set_managed(base_vdi_ref, False) 

879 util.SMlog("vdi_clone: introduced VDI: %s (%s)" % (base_vdi_ref, newuuid)) 

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

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

882 sm_config['vhd-parent'] = srcparent 

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

884 except Exception as e: 

885 util.SMlog("vdi_clone: caught error during VDI.db_introduce: %s" % (str(e))) 

886 # Note it's too late to actually clean stuff up here: the base disk has 

887 # been marked as deleted already. 

888 util.end_log_entry(self.sr.path, self.path, ["error"]) 

889 raise 

890 except util.CommandException as inst: 

891 # XXX: it might be too late if the base disk has been marked as deleted! 

892 self._clonecleanup(src, dst, newsrc) 

893 util.end_log_entry(self.sr.path, self.path, ["error"]) 

894 raise xs_errors.XenError('VDIClone', 

895 opterr='VDI clone failed error %d' % inst.code) 

896 

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

898 if snap_type == VDI.SNAPSHOT_DOUBLE and cbtlog: 898 ↛ 899line 898 didn't jump to line 899, because the condition on line 898 was never true

899 try: 

900 self._cbt_snapshot(dest, cbt_consistency) 

901 except: 

902 # CBT operation failed. 

903 util.end_log_entry(self.sr.path, self.path, ["error"]) 

904 raise 

905 

906 util.end_log_entry(self.sr.path, self.path, ["done"]) 

907 if snap_type != VDI.SNAPSHOT_INTERNAL: 907 ↛ 910line 907 didn't jump to line 910, because the condition on line 907 was never false

908 self.sr._update(self.sr.uuid, self.size) 

909 # Return info on the new user-visible leaf VDI 

910 ret_vdi = leaf_vdi 

911 if not ret_vdi: 911 ↛ 912line 911 didn't jump to line 912, because the condition on line 911 was never true

912 ret_vdi = base_vdi 

913 if not ret_vdi: 913 ↛ 914line 913 didn't jump to line 914, because the condition on line 913 was never true

914 ret_vdi = self 

915 return ret_vdi.get_params() 

916 

917 def get_params(self): 

918 if not self._checkpath(self.path): 

919 raise xs_errors.XenError('VDIUnavailable', \ 

920 opterr='VDI %s unavailable %s' % (self.uuid, self.path)) 

921 return super(FileVDI, self).get_params() 

922 

923 def _snap(self, child, parent): 

924 cmd = [SR.TAPDISK_UTIL, "snapshot", vhdutil.VDI_TYPE_VHD, child, parent] 

925 text = util.pread(cmd) 

926 

927 def _clonecleanup(self, src, dst, newsrc): 

928 try: 

929 if dst: 929 ↛ 933line 929 didn't jump to line 933, because the condition on line 929 was never false

930 util.ioretry(lambda: self._unlink(dst)) 

931 except util.CommandException as inst: 

932 pass 

933 try: 

934 if util.ioretry(lambda: util.pathexists(newsrc)): 934 ↛ exitline 934 didn't return from function '_clonecleanup', because the condition on line 934 was never false

935 stats = os.stat(newsrc) 

936 # Check if we have more than one link to newsrc 

937 if (stats.st_nlink > 1): 

938 util.ioretry(lambda: self._unlink(newsrc)) 

939 elif not self._is_hidden(newsrc): 939 ↛ exitline 939 didn't return from function '_clonecleanup', because the condition on line 939 was never false

940 self._rename(newsrc, src) 

941 except util.CommandException as inst: 

942 pass 

943 

944 def _checkpath(self, path): 

945 try: 

946 if not util.ioretry(lambda: util.pathexists(path)): 946 ↛ 947line 946 didn't jump to line 947, because the condition on line 946 was never true

947 return False 

948 return True 

949 except util.CommandException as inst: 

950 raise xs_errors.XenError('EIO', \ 

951 opterr='IO error checking path %s' % path) 

952 

953 def _query_v(self, path): 

954 cmd = [SR.TAPDISK_UTIL, "query", vhdutil.VDI_TYPE_VHD, "-v", path] 

955 return int(util.pread(cmd)) * 1024 * 1024 

956 

957 def _query_p_uuid(self, path): 

958 cmd = [SR.TAPDISK_UTIL, "query", vhdutil.VDI_TYPE_VHD, "-p", path] 

959 parent = util.pread(cmd) 

960 parent = parent[:-1] 

961 ls = parent.split('/') 

962 return ls[len(ls) - 1].replace(vhdutil.FILE_EXTN_VHD, '') 

963 

964 def _query_info(self, path, use_bkp_footer=False): 

965 diskinfo = {} 

966 qopts = '-vpf' 

967 if use_bkp_footer: 967 ↛ 969line 967 didn't jump to line 969, because the condition on line 967 was never false

968 qopts += 'b' 

969 cmd = [SR.TAPDISK_UTIL, "query", vhdutil.VDI_TYPE_VHD, qopts, path] 

970 txt = util.pread(cmd).split('\n') 

971 diskinfo['size'] = txt[0] 

972 lst = [txt[1].split('/')[-1].replace(vhdutil.FILE_EXTN_VHD, "")] 

973 for val in filter(util.exactmatch_uuid, lst): 973 ↛ 974line 973 didn't jump to line 974, because the loop on line 973 never started

974 diskinfo['parent'] = val 

975 diskinfo['hidden'] = txt[2].split()[1] 

976 return diskinfo 

977 

978 def _create(self, size, path): 

979 cmd = [SR.TAPDISK_UTIL, "create", vhdutil.VDI_TYPE_VHD, size, path] 

980 text = util.pread(cmd) 

981 if self.key_hash: 981 ↛ 982line 981 didn't jump to line 982, because the condition on line 981 was never true

982 vhdutil.setKey(path, self.key_hash) 

983 

984 def _mark_hidden(self, path): 

985 vhdutil.setHidden(path, True) 

986 self.hidden = 1 

987 

988 def _is_hidden(self, path): 

989 return vhdutil.getHidden(path) == 1 

990 

991 def extractUuid(path): 

992 fileName = os.path.basename(path) 

993 uuid = fileName.replace(vhdutil.FILE_EXTN_VHD, "") 

994 return uuid 

995 extractUuid = staticmethod(extractUuid) 

996 

997 def generate_config(self, sr_uuid, vdi_uuid): 

998 """ 

999 Generate the XML config required to attach and activate 

1000 a VDI for use when XAPI is not running. Attach and 

1001 activation is handled by vdi_attach_from_config below. 

1002 """ 

1003 util.SMlog("FileVDI.generate_config") 

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

1005 raise xs_errors.XenError('VDIUnavailable') 

1006 resp = {} 

1007 resp['device_config'] = self.sr.dconf 

1008 resp['sr_uuid'] = sr_uuid 

1009 resp['vdi_uuid'] = vdi_uuid 

1010 resp['command'] = 'vdi_attach_from_config' 

1011 # Return the 'config' encoded within a normal XMLRPC response so that 

1012 # we can use the regular response/error parsing code. 

1013 config = xmlrpc.client.dumps(tuple([resp]), "vdi_attach_from_config") 

1014 return xmlrpc.client.dumps((config, ), "", True) 

1015 

1016 def attach_from_config(self, sr_uuid, vdi_uuid): 

1017 """ 

1018 Attach and activate a VDI using config generated by 

1019 vdi_generate_config above. This is used for cases such as 

1020 the HA state-file and the redo-log. 

1021 """ 

1022 util.SMlog("FileVDI.attach_from_config") 

1023 try: 

1024 if not util.pathexists(self.sr.path): 

1025 self.sr.attach(sr_uuid) 

1026 except: 

1027 util.logException("FileVDI.attach_from_config") 

1028 raise xs_errors.XenError( 

1029 'SRUnavailable', 

1030 opterr='Unable to attach from config' 

1031 ) 

1032 

1033 def _create_cbt_log(self): 

1034 # Create CBT log file 

1035 # Name: <vdi_uuid>.cbtlog 

1036 #Handle if file already exists 

1037 log_path = self._get_cbt_logpath(self.uuid) 

1038 open_file = open(log_path, "w+") 

1039 open_file.close() 

1040 return super(FileVDI, self)._create_cbt_log() 

1041 

1042 def _delete_cbt_log(self): 

1043 logPath = self._get_cbt_logpath(self.uuid) 

1044 try: 

1045 os.remove(logPath) 

1046 except OSError as e: 

1047 if e.errno != errno.ENOENT: 

1048 raise 

1049 

1050 def _cbt_log_exists(self, logpath): 

1051 return util.pathexists(logpath) 

1052 

1053 

1054class SharedFileSR(FileSR): 

1055 """ 

1056 FileSR subclass for SRs that use shared network storage 

1057 """ 

1058 

1059 def _check_writable(self): 

1060 """ 

1061 Checks that the filesystem being used by the SR can be written to, 

1062 raising an exception if it can't. 

1063 """ 

1064 test_name = os.path.join(self.path, str(uuid4())) 

1065 try: 

1066 open(test_name, 'ab').close() 

1067 except OSError as e: 

1068 util.SMlog("Cannot write to SR file system: %s" % e) 

1069 raise xs_errors.XenError('SharedFileSystemNoWrite') 

1070 finally: 

1071 util.force_unlink(test_name) 

1072 

1073 def _raise_hardlink_error(self): 

1074 raise OSError(524, "Unknown error 524") 

1075 

1076 def _check_hardlinks(self): 

1077 hardlink_conf = self._read_hardlink_conf() 

1078 if hardlink_conf is not None: 1078 ↛ 1079line 1078 didn't jump to line 1079, because the condition on line 1078 was never true

1079 return hardlink_conf 

1080 

1081 test_name = os.path.join(self.path, str(uuid4())) 

1082 open(test_name, 'ab').close() 

1083 

1084 link_name = '%s.new' % test_name 

1085 try: 

1086 # XSI-1100: Let tests simulate failure of the link operation 

1087 util.fistpoint.activate_custom_fn( 

1088 "FileSR_fail_hardlink", 

1089 self._raise_hardlink_error) 

1090 

1091 os.link(test_name, link_name) 

1092 self._write_hardlink_conf(supported=True) 

1093 return True 

1094 except OSError: 

1095 self._write_hardlink_conf(supported=False) 

1096 

1097 msg = "File system for SR %s does not support hardlinks, crash " \ 

1098 "consistency of snapshots cannot be assured" % self.uuid 

1099 util.SMlog(msg, priority=util.LOG_WARNING) 

1100 # Note: session can be not set during attach/detach_from_config calls. 

1101 if self.session: 1101 ↛ 1110line 1101 didn't jump to line 1110, because the condition on line 1101 was never false

1102 try: 

1103 self.session.xenapi.message.create( 

1104 "sr_does_not_support_hardlinks", 2, "SR", self.uuid, 

1105 msg) 

1106 except XenAPI.Failure: 

1107 # Might already be set and checking has TOCTOU issues 

1108 pass 

1109 finally: 

1110 util.force_unlink(link_name) 

1111 util.force_unlink(test_name) 

1112 

1113 return False 

1114 

1115 def _get_hardlink_conf_path(self): 

1116 return os.path.join(self.path, 'sm-hardlink.conf') 

1117 

1118 def _read_hardlink_conf(self): 

1119 try: 

1120 with open(self._get_hardlink_conf_path(), 'r') as f: 

1121 try: 

1122 return bool(int(f.read())) 

1123 except Exception as e: 

1124 # If we can't read, assume the file is empty and test for hardlink support. 

1125 return None 

1126 except IOError as e: 

1127 if e.errno == errno.ENOENT: 

1128 # If the config file doesn't exist, assume we want to support hardlinks. 

1129 return None 

1130 util.SMlog('Failed to read hardlink conf: {}'.format(e)) 

1131 # Can be caused by a concurrent access, not a major issue. 

1132 return None 

1133 

1134 def _write_hardlink_conf(self, supported): 

1135 try: 

1136 with open(self._get_hardlink_conf_path(), 'w') as f: 

1137 f.write('1' if supported else '0') 

1138 except Exception as e: 

1139 # Can be caused by a concurrent access, not a major issue. 

1140 util.SMlog('Failed to write hardlink conf: {}'.format(e)) 

1141 

1142# SR registration at import 

1143SR.registerSR(FileSR)