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(r"\.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 r"([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(r"[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 # pylint: disable=import-error 

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 smb3_fail_reason = None 

324 try: 

325 if protocol == 'nfs_iso': 

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

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

328 # to the process waiting. 

329 serv_path = [] 

330 transport = 'tcp' 

331 if location.startswith('['): 331 ↛ 333line 331 didn't jump to line 333, because the condition on line 331 was never true

332 # IPv6 target: remove brackets around the IPv6 

333 transport = 'tcp6' 

334 ip6 = location[1:location.index(']')] 

335 path = location[location.index(']') + 2:] 

336 serv_path = [ip6, path] 

337 else: 

338 serv_path = location.split(':') 

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

340 # Extract timeout and retrans values, if any 

341 io_timeout = nfs.get_nfs_timeout(self.other_config) 

342 io_retrans = nfs.get_nfs_retrans(self.other_config) 

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

344 transport, useroptions=options, nfsversion=self.nfsversion, 

345 timeout=io_timeout, retrans=io_retrans) 

346 else: 

347 if self.smbversion in SMB_VERSION_3: 

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

349 try: 

350 self.mountOverSMB(mountcmd) 

351 except util.CommandException as inst: 

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

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

354 smb3_fail_reason = inst.reason 

355 # mountcmd is constructed such that the last two 

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

357 del mountcmd[-2:] 

358 self.smbversion = SMB_VERSION_1 

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

360 self.appendCIFSMountOptions(mountcmd) 

361 else: 

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

363 # regex can be used here since we have 

364 # already validated version entry 

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

366 options[1]) 

367 mountcmd.extend(options) 

368 self.mountOverSMB(mountcmd) 

369 else: 

370 raise xs_errors.XenError( 

371 'ISOMountFailure', opterr=inst.reason) 

372 else: 

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

374 self.mountOverSMB(mountcmd) 

375 except util.CommandException as inst: 

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

377 raise xs_errors.XenError( 

378 'ISOMountFailure', opterr=smb3_fail_reason) 

379 else: 

380 raise xs_errors.XenError( 

381 'ISOMountFailure', opterr=inst.reason) 

382 except nfs.NfsException as e: 

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

384 

385 # Check the iso_path is accessible 

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

387 self.detach(sr_uuid) 

388 raise xs_errors.XenError('ISOSharenameFailure') 

389 

390 def after_master_attach(self, uuid): 

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

392 Return: 

393 None 

394 """ 

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

396 pass 

397 

398 def getSMBVersionFromOptions(self, options): 

399 """Extract SMB version from options """ 

400 smb_ver = None 

401 options_list = options.split(',') 

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

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

404 version = option.split('=') 

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

406 smb_ver = version[1] 

407 break 

408 return smb_ver 

409 

410 def getSMBVersion(self): 

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

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

413 return smbversion 

414 

415 def mountOverSMB(self, mountcmd): 

416 """This function raises util.CommandException""" 

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

418 prefix="cifs") 

419 

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

421 try: 

422 if not self.is_smbversion_specified: 

423 # Store the successful smb version in PBD config 

424 self.updateSMBVersInPBDConfig() 

425 except Exception as exc: 

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

427 if self._checkmount(): 

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

429 raise util.CommandException 

430 

431 def updateSMBVersInPBDConfig(self): 

432 """Store smb version in PBD config""" 

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

434 if pbd is not None: 

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

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

437 dconf['vers'] = self.smbversion 

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

439 else: 

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

441 

442 def getNFSOptions(self, options): 

443 """Append options to mount.nfs""" 

444 #Only return any options specified with -o 

445 nfsOptions = '' 

446 for index, opt in enumerate(options): 

447 if opt == "-o": 

448 nfsOptions = options[index + 1] 

449 break 

450 

451 return nfsOptions 

452 

453 def appendCIFSMountOptions(self, mountcmd): 

454 """Append options to mount.cifs""" 

455 options = [] 

456 try: 

457 options.append(self.getCacheOptions()) 

458 

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

460 options.append('guest') 

461 

462 options.append(self.getSMBVersion()) 

463 

464 username, domain = ( 

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

466 ) 

467 

468 if domain: 

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

470 except: 

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

472 raise 

473 

474 # Extend mountcmd appropriately 

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

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

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

478 

479 def getCacheOptions(self): 

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

481 return "cache=none" 

482 

483 def detach(self, sr_uuid): 

484 """Std. detach""" 

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

486 return 

487 

488 try: 

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

490 except util.CommandException as inst: 

491 raise xs_errors.XenError('NFSUnMount', \ 

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

493 

494 def scan(self, sr_uuid): 

495 """Scan: see _loadvdis""" 

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

497 raise xs_errors.XenError('SRUnavailable', \ 

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

499 

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

501 raise xs_errors.XenError('SRUnavailable', \ 

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

503 

504 #try: 

505 if not self.vdis: 

506 self._loadvdis() 

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

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

509 self.virtual_allocation = self.physical_size 

510 

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

512 

513 if 'xenserver_tools_sr' in self.other_config and \ 

514 self.other_config['xenserver_tools_sr'] == "true": 

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

516 # only one as the official one. 

517 

518 # Pass 1: find the latest version 

519 latest_build_vdi = None 

520 latest_build_number = "0" 

521 for vdi_name in self.vdis: 

522 vdi = self.vdis[vdi_name] 

523 

524 if latest_build_vdi is None: 

525 latest_build_vdi = vdi.location 

526 latest_build_number = "0" 

527 

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

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

530 if bld >= latest_build_number: 

531 latest_build_vdi = vdi.location 

532 latest_build_number = bld 

533 

534 # Pass 2: mark all VDIs accordingly 

535 for vdi_name in self.vdis: 

536 vdi = self.vdis[vdi_name] 

537 if vdi.location == latest_build_vdi: 

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

539 else: 

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

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

542 

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

544 scanrecord = SR.ScanRecord(self) 

545 scanrecord.synchronise_new() 

546 scanrecord.synchronise_existing() 

547 

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

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

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

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

552 for vdi_ref in all_vdis.keys(): 

553 vdi = all_vdis[vdi_ref] 

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

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

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

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

558 else: 

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

560 

561 

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

563 for location in scanrecord.gone: 

564 vdi = scanrecord.get_xenapi_vdi(location) 

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

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

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

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

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

570 # Mark it as missing for informational purposes only 

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

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

573 

574 else: 

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

576 

577 def create(self, sr_uuid, size): 

578 self.attach(sr_uuid) 

579 if 'type' in self.dconf: 

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

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

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

583 

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

585 # characters. 

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

587 if is_image_utf8_compatible(f): 

588 try: 

589 f.encode('ascii') 

590 except UnicodeEncodeError: 

591 raise xs_errors.XenError('CIFSExtendedCharsNotSupported', 

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

593 

594 self.detach(sr_uuid) 

595 

596 

597class ISOVDI(VDI.VDI): 

598 def load(self, vdi_uuid): 

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

600 # will fail. 

601 self.vdi_type = "iso" 

602 try: 

603 stat = os.stat(self.path) 

604 self.utilisation = int(stat.st_size) 

605 self.size = int(stat.st_size) 

606 self.label = self.filename 

607 except: 

608 pass 

609 

610 def __init__(self, mysr, filename): 

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

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

613 self.location = filename 

614 self.filename = filename 

615 self.read_only = True 

616 self.label = filename 

617 self.sm_config = {} 

618 if "legacy_mode" in mysr.dconf: 

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

620 self.label = tools_iso_name(filename) 

621 # Mark this as a Tools CD 

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

623 # Extract a version string, if present 

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

625 # "4.1.0" 

626 if len(vsn) == 1: 

627 build_number = "0" # string 

628 product_version = vsn[0] 

629 # "4.1.0-1234" 

630 elif len(vsn) > 1: 

631 build_number = vsn[1] 

632 product_version = vsn[0] 

633 else: 

634 build_number = 0 

635 product_version = "unknown" 

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

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

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

639 

640 def detach(self, sr_uuid, vdi_uuid): 

641 pass 

642 

643 def attach(self, sr_uuid, vdi_uuid): 

644 try: 

645 os.stat(self.path) 

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

647 except: 

648 raise xs_errors.XenError('VDIMissing') 

649 

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

651 self.uuid = vdi_uuid 

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

653 self.size = size 

654 self.utilisation = 0 

655 self.read_only = False 

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

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

658 

659 if util.pathexists(self.path): 

660 raise xs_errors.XenError('VDIExists') 

661 

662 try: 

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

664 handle.truncate(size) 

665 handle.close() 

666 self._db_introduce() 

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

668 except Exception as exn: 

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

670 raise xs_errors.XenError('VDICreate', \ 

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

672 

673 def delete(self, sr_uuid, vdi_uuid): 

674 util.SMlog("Deleting...") 

675 

676 self.uuid = vdi_uuid 

677 self._db_forget() 

678 

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

680 return 

681 

682 try: 

683 util.SMlog("Unlinking...") 

684 os.unlink(self.path) 

685 util.SMlog("Done...") 

686 except: 

687 raise xs_errors.XenError('VDIDelete') 

688 

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

690 # exceptions 

691 

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

693 SRCommand.run(ISOSR, DRIVER_INFO) 

694else: 

695 SR.registerSR(ISOSR)