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# ISOSR: remote iso storage repository 

19 

20import SR 

21import VDI 

22import SRCommand 

23import util 

24import nfs 

25import os 

26import re 

27import xs_errors 

28import cifutils 

29 

30CAPABILITIES = ["VDI_CREATE", "VDI_DELETE", "VDI_ATTACH", "VDI_DETACH", 

31 "SR_SCAN", "SR_ATTACH", "SR_DETACH"] 

32 

33CONFIGURATION = \ 

34 [['location', 'path to mount (required) (e.g. server:/path)'], 

35 ['options', 

36 'extra options to pass to mount (deprecated) (e.g. \'-o ro\')'], 

37 ['type', 'cifs or nfs'], 

38 nfs.NFS_VERSION] 

39 

40DRIVER_INFO = { 

41 'name': 'ISO', 

42 'description': 'Handles CD images stored as files in iso format', 

43 'vendor': 'Citrix Systems Inc', 

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

45 'driver_version': '1.0', 

46 'required_api_version': '1.0', 

47 'capabilities': CAPABILITIES, 

48 'configuration': CONFIGURATION 

49 } 

50 

51TYPE = "iso" 

52SMB_VERSION_1 = '1.0' 

53SMB_VERSION_3 = '3.0' 

54NFSPORT = 2049 

55 

56 

57def is_image_utf8_compatible(s): 

58 # pylint: disable=no-member 

59 regex = re.compile("\.iso$|\.img$", re.I) 

60 if regex.search(s) is None: 

61 return False 

62 

63 # Check for extended characters 

64 if type(s) == str: 

65 try: 

66 s.encode('utf-8') 

67 except UnicodeEncodeError as e: 

68 util.SMlog("WARNING: This string is not UTF-8 compatible.") 

69 return False 

70 return True 

71 

72 

73def tools_iso_name(filename): 

74 # The tools ISO used have a "xs-" prefix in its name. 

75 # We recognise both and set the name_label accordingly. 

76 if filename[:3] == "xs-": 

77 return "xs-tools.iso" 

78 else: 

79 return "guest-tools.iso" 

80 

81 

82class ISOSR(SR.SR): 

83 """Local file storage repository""" 

84 

85 # Some helper functions: 

86 def _checkmount(self): 

87 """Checks that the mountpoint exists and is mounted""" 

88 if not util.pathexists(self.mountpoint): 88 ↛ 90line 88 didn't jump to line 90, because the condition on line 88 was never false

89 return False 

90 try: 

91 ismount = util.ismount(self.mountpoint) 

92 except util.CommandException as inst: 

93 return False 

94 return ismount 

95 

96 def _checkTargetStr(self, location): 

97 if 'type' not in self.dconf: 

98 return 

99 if self.dconf['type'] == 'cifs': 99 ↛ 100line 99 didn't jump to line 100, because the condition on line 99 was never true

100 tgt = '' 

101 if re.search('^//', location): 

102 tgt = location.split('/')[2] 

103 elif re.search(r'^\\', location): 

104 l = location.split('\\') 

105 for i in location.split('\\'): 

106 if i: 

107 tgt = i 

108 break 

109 if not tgt: 

110 raise xs_errors.XenError('ISOLocationStringError') 

111 else: 

112 if location.find(':') == -1: 112 ↛ 113line 112 didn't jump to line 113, because the condition on line 112 was never true

113 raise xs_errors.XenError('ISOLocationStringError') 

114 tgt = location.split(':')[0] 

115 

116 try: 

117 util._convertDNS(tgt) 

118 except: 

119 raise xs_errors.XenError('DNSError') 

120 

121 # pylint: disable=no-member 

122 uuid_file_regex = re.compile( 

123 "([0-9a-f]{8}-(([0-9a-f]{4})-){3}[0-9a-f]{12})\.(iso|img)", re.I) 

124 

125 def _loadvdis(self): 

126 """Scan the directory and get uuids either from the VDI filename, \ 

127 or by creating a new one.""" 

128 if self.vdis: 

129 return 

130 

131 for name in filter(is_image_utf8_compatible, 

132 util.listdir(self.path, quiet=True)): 

133 fileName = self.path + "/" + name 

134 if os.path.isdir(fileName): 

135 util.SMlog("_loadvdis : %s is a directory. Ignore" % fileName) 

136 continue 

137 

138 # CA-80254: Check for iso/img files whose name consists of extended 

139 # characters. 

140 try: 

141 name.encode('ascii') 

142 except UnicodeEncodeError: 

143 raise xs_errors.XenError('CIFSExtendedCharsNotSupported', \ 

144 opterr='The repository contains at least one file whose name consists of extended characters.') 

145 

146 self.vdis[name] = ISOVDI(self, name) 

147 # Set the VDI UUID if the filename is of the correct form. 

148 # Otherwise, one will be generated later in VDI._db_introduce. 

149 m = self.uuid_file_regex.match(name) 

150 if m: 

151 self.vdis[name].uuid = m.group(1) 

152 

153 # Synchronise the read-only status with existing VDI records 

154 __xenapi_records = util.list_VDI_records_in_sr(self) 

155 __xenapi_locations = {} 

156 for vdi in __xenapi_records.keys(): 

157 __xenapi_locations[__xenapi_records[vdi]['location']] = vdi 

158 for vdi in self.vdis.values(): 

159 if vdi.location in __xenapi_locations: 

160 v = __xenapi_records[__xenapi_locations[vdi.location]] 

161 sm_config = v['sm_config'] 

162 if 'created' in sm_config: 

163 vdi.sm_config['created'] = sm_config['created'] 

164 vdi.read_only = False 

165 

166# Now for the main functions: 

167 def handles(type): 

168 """Do we handle this type?""" 

169 if type == TYPE: 

170 return True 

171 return False 

172 handles = staticmethod(handles) 

173 

174 def content_type(self, sr_uuid): 

175 """Returns the content_type XML""" 

176 return super(ISOSR, self).content_type(sr_uuid) 

177 

178 # pylint: disable=no-member 

179 vdi_path_regex = re.compile("[a-z0-9.-]+\.(iso|img)", re.I) 

180 

181 def vdi(self, uuid): 

182 """Create a VDI class. If the VDI does not exist, we determine 

183 here what its filename should be.""" 

184 

185 filename = util.to_plain_string(self.srcmd.params.get('vdi_location')) 

186 if filename is None: 

187 smconfig = self.srcmd.params.get('vdi_sm_config') 

188 if smconfig is None: 

189 # uh, oh, a VDI.from_uuid() 

190 import XenAPI 

191 _VDI = self.session.xenapi.VDI 

192 try: 

193 vdi_ref = _VDI.get_by_uuid(uuid) 

194 except XenAPI.Failure as e: 

195 if e.details[0] != 'UUID_INVALID': 

196 raise 

197 else: 

198 filename = _VDI.get_location(vdi_ref) 

199 

200 if filename is None: 

201 # Get the filename from sm-config['path'], or use the UUID 

202 # if the path param doesn't exist. 

203 if smconfig and 'path' in smconfig: 

204 filename = smconfig['path'] 

205 if not self.vdi_path_regex.match(filename): 

206 raise xs_errors.XenError('VDICreate', \ 

207 opterr='Invalid path "%s"' % filename) 

208 else: 

209 filename = '%s.img' % uuid 

210 

211 return ISOVDI(self, filename) 

212 

213 def load(self, sr_uuid): 

214 """Initialises the SR""" 

215 # First of all, check we've got the correct keys in dconf 

216 if 'location' not in self.dconf: 216 ↛ 217line 216 didn't jump to line 217, because the condition on line 216 was never true

217 raise xs_errors.XenError('ConfigLocationMissing') 

218 

219 # Construct the path we're going to mount under: 

220 if "legacy_mode" in self.dconf: 

221 self.mountpoint = util.to_plain_string(self.dconf['location']) 

222 else: 

223 # Verify the target address 

224 self._checkTargetStr(self.dconf['location']) 

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

226 

227 # Add on the iso_path value if there is one 

228 if "iso_path" in self.dconf: 228 ↛ 229line 228 didn't jump to line 229, because the condition on line 228 was never true

229 iso_path = util.to_plain_string(self.dconf['iso_path']) 

230 if iso_path.startswith("/"): 

231 iso_path = iso_path[1:] 

232 self.path = os.path.join(self.mountpoint, iso_path) 

233 else: 

234 self.path = self.mountpoint 

235 

236 # Handle optional dconf attributes 

237 self.nfsversion = nfs.validate_nfsversion(self.dconf.get('nfsversion')) 

238 

239 # Fill the required SMB version 

240 self.smbversion = SMB_VERSION_3 

241 

242 # Check if smb version is specified from client 

243 self.is_smbversion_specified = False 

244 

245 # Some info we need: 

246 self.sr_vditype = 'phy' 

247 

248 def delete(self, sr_uuid): 

249 pass 

250 

251 def attach(self, sr_uuid): 

252 """Std. attach""" 

253 # Very-Legacy mode means the ISOs are in the local fs - so no need to attach. 

254 if 'legacy_mode' in self.dconf: 

255 # Verify path exists 

256 if not os.path.exists(self.mountpoint): 

257 raise xs_errors.XenError('ISOLocalPath') 

258 return 

259 

260 # Check whether we're already mounted 

261 if self._checkmount(): 

262 return 

263 

264 # Create the mountpoint if it's not already there 

265 if not util.isdir(self.mountpoint): 265 ↛ 268line 265 didn't jump to line 268, because the condition on line 265 was never false

266 util.makedirs(self.mountpoint) 

267 

268 mountcmd = [] 

269 location = util.to_plain_string(self.dconf['location']) 

270 # TODO: Have XC standardise iso type string 

271 protocol = 'nfs_iso' 

272 options = '' 

273 

274 if 'type' in self.dconf: 

275 protocol = self.dconf['type'] 

276 elif ":/" not in location: 276 ↛ 279line 276 didn't jump to line 279, because the condition on line 276 was never false

277 protocol = 'cifs' 

278 

279 if 'options' in self.dconf: 

280 options = self.dconf['options'].split(' ') 

281 if protocol == 'cifs': 281 ↛ 284line 281 didn't jump to line 284, because the condition on line 281 was never false

282 options = [x for x in options if x != ""] 

283 else: 

284 options = self.getNFSOptions(options) 

285 

286 # SMB options are passed differently for create via 

287 # XC/xe sr-create and create via xe-mount-iso-sr 

288 # In both cases check if SMB version is passed are not. 

289 # If not use self.smbversion. 

290 if protocol == 'cifs': 

291 if 'type' in self.dconf: 

292 # Create via XC or sr-create 

293 # Check for username and password 

294 mountcmd = ["mount.cifs", location, self.mountpoint] 

295 if 'vers' in self.dconf: 

296 self.is_smbversion_specified = True 

297 self.smbversion = self.dconf['vers'] 

298 util.SMlog("self.dconf['vers'] = %s" % self.dconf['vers']) 

299 self.appendCIFSMountOptions(mountcmd) 

300 else: 

301 # Creation via xe-mount-iso-sr 

302 try: 

303 mountcmd = ["mount", location, self.mountpoint] 

304 if options and options[0] == '-o': 304 ↛ 313line 304 didn't jump to line 313, because the condition on line 304 was never false

305 pos = options[1].find('vers=') 

306 if pos == -1: 306 ↛ 307line 306 didn't jump to line 307, because the condition on line 306 was never true

307 options[1] += ',' + self.getSMBVersion() 

308 else: 

309 self.smbversion = self.getSMBVersionFromOptions( 

310 options[1]) 

311 self.is_smbversion_specified = True 

312 else: 

313 raise ValueError 

314 mountcmd.extend(options) 

315 except ValueError: 

316 raise xs_errors.XenError('ISOInvalidXeMountOptions') 

317 # Check the validity of 'smbversion'. 

318 # Raise an exception for any invalid version. 

319 if self.smbversion not in [SMB_VERSION_1, SMB_VERSION_3]: 

320 raise xs_errors.XenError('ISOInvalidSMBversion') 

321 

322 # Attempt mounting 

323 try: 

324 if protocol == 'nfs_iso': 

325 # For NFS, do a soft mount with tcp as protocol. Since ISO SR is 

326 # going to be r-only, a failure in nfs link can be reported back 

327 # to the process waiting. 

328 serv_path = location.split(':') 

329 util._testHost(serv_path[0], NFSPORT, 'NFSTarget') 

330 nfs.soft_mount(self.mountpoint, serv_path[0], serv_path[1], 

331 'tcp', useroptions=options, 

332 nfsversion=self.nfsversion) 

333 else: 

334 smb3_fail_reason = None 

335 if self.smbversion in SMB_VERSION_3: 

336 util.SMlog('ISOSR mount over smb 3.0') 

337 try: 

338 self.mountOverSMB(mountcmd) 

339 except util.CommandException as inst: 

340 if not self.is_smbversion_specified: 340 ↛ 358line 340 didn't jump to line 358, because the condition on line 340 was never false

341 util.SMlog('Retrying ISOSR mount over smb 1.0') 

342 smb3_fail_reason = inst.reason 

343 # mountcmd is constructed such that the last two 

344 # items will contain -o argument and its value. 

345 del mountcmd[-2:] 

346 self.smbversion = SMB_VERSION_1 

347 if not options: 347 ↛ 350line 347 didn't jump to line 350, because the condition on line 347 was never false

348 self.appendCIFSMountOptions(mountcmd) 

349 else: 

350 if options[0] == '-o': 

351 # regex can be used here since we have 

352 # already validated version entry 

353 options[1] = re.sub('vers=3.0', 'vers=1.0', 

354 options[1]) 

355 mountcmd.extend(options) 

356 self.mountOverSMB(mountcmd) 

357 else: 

358 raise xs_errors.XenError( 

359 'ISOMountFailure', opterr=inst.reason) 

360 else: 

361 util.SMlog('ISOSR mount over smb 1.0') 

362 self.mountOverSMB(mountcmd) 

363 except util.CommandException as inst: 

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

365 raise xs_errors.XenError( 

366 'ISOMountFailure', opterr=smb3_fail_reason) 

367 else: 

368 raise xs_errors.XenError( 

369 'ISOMountFailure', opterr=inst.reason) 

370 except nfs.NfsException as e: 

371 raise xs_errors.XenError('ISOMountFailure', opterr=str(e.errstr)) 

372 

373 # Check the iso_path is accessible 

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

375 self.detach(sr_uuid) 

376 raise xs_errors.XenError('ISOSharenameFailure') 

377 

378 def after_master_attach(self, uuid): 

379 """Perform actions required after attaching on the pool master 

380 Return: 

381 None 

382 """ 

383 # Nothing required here for ISOs and tools ISOs will fail if scanned 

384 pass 

385 

386 def getSMBVersionFromOptions(self, options): 

387 """Extract SMB version from options """ 

388 smb_ver = None 

389 options_list = options.split(',') 

390 for option in options_list: 390 ↛ 396line 390 didn't jump to line 396, because the loop on line 390 didn't complete

391 if option.startswith('vers='): 391 ↛ 390line 391 didn't jump to line 390, because the condition on line 391 was never false

392 version = option.split('=') 

393 if len(version) == 2: 393 ↛ 395line 393 didn't jump to line 395, because the condition on line 393 was never false

394 smb_ver = version[1] 

395 break 

396 return smb_ver 

397 

398 def getSMBVersion(self): 

399 """Pass smb version option to mount.cifs""" 

400 smbversion = "vers=%s" % self.smbversion 

401 return smbversion 

402 

403 def mountOverSMB(self, mountcmd): 

404 """This function raises util.CommandException""" 

405 new_env, domain = cifutils.getCIFCredentials(self.dconf, self.session, 

406 prefix="cifs") 

407 

408 util.pread(mountcmd, True, new_env=new_env) 

409 try: 

410 if not self.is_smbversion_specified: 

411 # Store the successful smb version in PBD config 

412 self.updateSMBVersInPBDConfig() 

413 except Exception as exc: 

414 util.SMlog("Exception: %s" % str(exc)) 

415 if self._checkmount(): 

416 util.pread(["umount", self.mountpoint]) 

417 raise util.CommandException 

418 

419 def updateSMBVersInPBDConfig(self): 

420 """Store smb version in PBD config""" 

421 pbd = util.find_my_pbd(self.session, self.host_ref, self.sr_ref) 

422 if pbd is not None: 

423 util.SMlog('Updating SMB version in PBD device config') 

424 dconf = self.session.xenapi.PBD.get_device_config(pbd) 

425 dconf['vers'] = self.smbversion 

426 self.session.xenapi.PBD.set_device_config(pbd, dconf) 

427 else: 

428 raise Exception('Could not find PBD for corresponding SR') 

429 

430 def getNFSOptions(self, options): 

431 """Append options to mount.nfs""" 

432 #Only return any options specified with -o 

433 nfsOptions = '' 

434 for index, opt in enumerate(options): 

435 if opt == "-o": 

436 nfsOptions = options[index + 1] 

437 break 

438 

439 return nfsOptions 

440 

441 def appendCIFSMountOptions(self, mountcmd): 

442 """Append options to mount.cifs""" 

443 options = [] 

444 try: 

445 options.append(self.getCacheOptions()) 

446 

447 if not cifutils.containsCredentials(self.dconf, prefix="cifs"): 

448 options.append('guest') 

449 

450 options.append(self.getSMBVersion()) 

451 

452 username, domain = ( 

453 cifutils.splitDomainAndUsername(self.dconf['username']) 

454 ) 

455 

456 if domain: 

457 options.append('domain=' + domain) 

458 except: 

459 util.SMlog("Exception while attempting to append mount options") 

460 raise 

461 

462 # Extend mountcmd appropriately 

463 if options: 463 ↛ exitline 463 didn't return from function 'appendCIFSMountOptions', because the condition on line 463 was never false

464 options = ",".join(str(x) for x in options if x) 

465 mountcmd.extend(["-o", options]) 

466 

467 def getCacheOptions(self): 

468 """Pass cache options to mount.cifs""" 

469 return "cache=none" 

470 

471 def detach(self, sr_uuid): 

472 """Std. detach""" 

473 if 'legacy_mode' in self.dconf or not self._checkmount(): 473 ↛ 476line 473 didn't jump to line 476, because the condition on line 473 was never false

474 return 

475 

476 try: 

477 util.pread(["umount", self.mountpoint]) 

478 except util.CommandException as inst: 

479 raise xs_errors.XenError('NFSUnMount', \ 

480 opterr='error is %d' % inst.code) 

481 

482 def scan(self, sr_uuid): 

483 """Scan: see _loadvdis""" 

484 if not util.isdir(self.path): 

485 raise xs_errors.XenError('SRUnavailable', \ 

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

487 

488 if ('legacy_mode' not in self.dconf) and (not self._checkmount()): 

489 raise xs_errors.XenError('SRUnavailable', \ 

490 opterr='directory not mounted: %s' % self.path) 

491 

492 #try: 

493 if not self.vdis: 

494 self._loadvdis() 

495 self.physical_size = util.get_fs_size(self.path) 

496 self.physical_utilisation = util.get_fs_utilisation(self.path) 

497 self.virtual_allocation = self.physical_size 

498 

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

500 

501 if 'xenserver_tools_sr' in other_config and \ 

502 other_config['xenserver_tools_sr'] == "true": 

503 # Out of all the xs-tools ISOs which exist in this dom0, we mark 

504 # only one as the official one. 

505 

506 # Pass 1: find the latest version 

507 latest_build_vdi = None 

508 latest_build_number = "0" 

509 for vdi_name in self.vdis: 

510 vdi = self.vdis[vdi_name] 

511 

512 if latest_build_vdi is None: 

513 latest_build_vdi = vdi.location 

514 latest_build_number = "0" 

515 

516 if 'xs-tools-build' in vdi.sm_config: 

517 bld = vdi.sm_config['xs-tools-build'] 

518 if bld >= latest_build_number: 

519 latest_build_vdi = vdi.location 

520 latest_build_number = bld 

521 

522 # Pass 2: mark all VDIs accordingly 

523 for vdi_name in self.vdis: 

524 vdi = self.vdis[vdi_name] 

525 if vdi.location == latest_build_vdi: 

526 vdi.sm_config['xs-tools'] = "true" 

527 else: 

528 if "xs-tools" in vdi.sm_config: 

529 del vdi.sm_config['xs-tools'] 

530 

531 # Synchronise the VDIs: this will update the sm_config maps of current records 

532 scanrecord = SR.ScanRecord(self) 

533 scanrecord.synchronise_new() 

534 scanrecord.synchronise_existing() 

535 

536 # Everything that looks like an xs-tools ISO but which isn't the 

537 # primary one will also be renamed "Old version of ..." 

538 sr = self.session.xenapi.SR.get_by_uuid(sr_uuid) 

539 all_vdis = self.session.xenapi.VDI.get_all_records_where("field \"SR\" = \"%s\"" % sr) 

540 for vdi_ref in all_vdis.keys(): 

541 vdi = all_vdis[vdi_ref] 

542 if 'xs-tools-version' in vdi['sm_config']: 

543 name = tools_iso_name(vdi['location']) 

544 if 'xs-tools' in vdi['sm_config']: 

545 self.session.xenapi.VDI.set_name_label(vdi_ref, name) 

546 else: 

547 self.session.xenapi.VDI.set_name_label(vdi_ref, "Old version of " + name) 

548 

549 

550 # never forget old VDI records to cope with rolling upgrade 

551 for location in scanrecord.gone: 

552 vdi = scanrecord.get_xenapi_vdi(location) 

553 util.SMlog("Marking previous version of tools ISO: location=%s uuid=%s" % (vdi['location'], vdi['uuid'])) 

554 vdi = self.session.xenapi.VDI.get_by_uuid(vdi['uuid']) 

555 name_label = self.session.xenapi.VDI.get_name_label(vdi) 

556 if not(name_label.startswith("Old version of ")): 

557 self.session.xenapi.VDI.set_name_label(vdi, "Old version of " + name_label) 

558 # Mark it as missing for informational purposes only 

559 self.session.xenapi.VDI.set_missing(vdi, True) 

560 self.session.xenapi.VDI.remove_from_sm_config(vdi, 'xs-tools') 

561 

562 else: 

563 return super(ISOSR, self).scan(sr_uuid) 

564 

565 def create(self, sr_uuid, size): 

566 self.attach(sr_uuid) 

567 if 'type' in self.dconf: 

568 smconfig = self.session.xenapi.SR.get_sm_config(self.sr_ref) 

569 smconfig['iso_type'] = self.dconf['type'] 

570 self.session.xenapi.SR.set_sm_config(self.sr_ref, smconfig) 

571 

572 # CA-80254: Check for iso/img files whose name consists of extended 

573 # characters. 

574 for f in util.listdir(self.path, quiet=True): 

575 if is_image_utf8_compatible(f): 

576 try: 

577 f.encode('ascii') 

578 except UnicodeEncodeError: 

579 raise xs_errors.XenError('CIFSExtendedCharsNotSupported', 

580 opterr='The repository contains at least one file whose name consists of extended characters.') 

581 

582 self.detach(sr_uuid) 

583 

584 

585class ISOVDI(VDI.VDI): 

586 def load(self, vdi_uuid): 

587 # Nb, in the vdi_create call, the filename is unset, so the following 

588 # will fail. 

589 self.vdi_type = "iso" 

590 try: 

591 stat = os.stat(self.path) 

592 self.utilisation = int(stat.st_size) 

593 self.size = int(stat.st_size) 

594 self.label = self.filename 

595 except: 

596 pass 

597 

598 def __init__(self, mysr, filename): 

599 self.path = os.path.join(mysr.path, filename) 

600 VDI.VDI.__init__(self, mysr, None) 

601 self.location = filename 

602 self.filename = filename 

603 self.read_only = True 

604 self.label = filename 

605 self.sm_config = {} 

606 if "legacy_mode" in mysr.dconf: 

607 if filename.startswith("xs-tools") or filename.startswith("guest-tools"): 

608 self.label = tools_iso_name(filename) 

609 # Mark this as a Tools CD 

610 # self.sm_config['xs-tools'] = 'true' 

611 # Extract a version string, if present 

612 vsn = filename[filename.find("tools") + len("tools"):][:-len(".iso")].strip("-").split("-", 1) 

613 # "4.1.0" 

614 if len(vsn) == 1: 

615 build_number = "0" # string 

616 product_version = vsn[0] 

617 # "4.1.0-1234" 

618 elif len(vsn) > 1: 

619 build_number = vsn[1] 

620 product_version = vsn[0] 

621 else: 

622 build_number = 0 

623 product_version = "unknown" 

624 util.SMlog("version=%s build=%s" % (product_version, build_number)) 

625 self.sm_config['xs-tools-version'] = product_version 

626 self.sm_config['xs-tools-build'] = build_number 

627 

628 def detach(self, sr_uuid, vdi_uuid): 

629 pass 

630 

631 def attach(self, sr_uuid, vdi_uuid): 

632 try: 

633 os.stat(self.path) 

634 return super(ISOVDI, self).attach(sr_uuid, vdi_uuid) 

635 except: 

636 raise xs_errors.XenError('VDIMissing') 

637 

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

639 self.uuid = vdi_uuid 

640 self.path = os.path.join(self.sr.path, self.filename) 

641 self.size = size 

642 self.utilisation = 0 

643 self.read_only = False 

644 self.sm_config = self.sr.srcmd.params['vdi_sm_config'] 

645 self.sm_config['created'] = util._getDateString() 

646 

647 if util.pathexists(self.path): 

648 raise xs_errors.XenError('VDIExists') 

649 

650 try: 

651 handle = open(self.path, "w") 

652 handle.truncate(size) 

653 handle.close() 

654 self._db_introduce() 

655 return super(ISOVDI, self).get_params() 

656 except Exception as exn: 

657 util.SMlog("Exception when creating VDI: %s" % exn) 

658 raise xs_errors.XenError('VDICreate', \ 

659 opterr='could not create file: "%s"' % self.path) 

660 

661 def delete(self, sr_uuid, vdi_uuid): 

662 util.SMlog("Deleting...") 

663 

664 self.uuid = vdi_uuid 

665 self._db_forget() 

666 

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

668 return 

669 

670 try: 

671 util.SMlog("Unlinking...") 

672 os.unlink(self.path) 

673 util.SMlog("Done...") 

674 except: 

675 raise xs_errors.XenError('VDIDelete') 

676 

677 # delete, update, introduce unimplemented. super class will raise 

678 # exceptions 

679 

680if __name__ == '__main__': 680 ↛ 681line 680 didn't jump to line 681, because the condition on line 680 was never true

681 SRCommand.run(ISOSR, DRIVER_INFO) 

682else: 

683 SR.registerSR(ISOSR)