Coverage for drivers/ISOSR.py : 37%

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
20import SR
21import VDI
22import SRCommand
23import util
24import nfs
25import os
26import re
27import xs_errors
28import cifutils
30CAPABILITIES = ["VDI_CREATE", "VDI_DELETE", "VDI_ATTACH", "VDI_DETACH",
31 "SR_SCAN", "SR_ATTACH", "SR_DETACH"]
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]
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 }
51TYPE = "iso"
52SMB_VERSION_1 = '1.0'
53SMB_VERSION_3 = '3.0'
54NFSPORT = 2049
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
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
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"
82class ISOSR(SR.SR):
83 """Local file storage repository"""
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
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]
116 try:
117 util._convertDNS(tgt)
118 except:
119 raise xs_errors.XenError('DNSError')
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)
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
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
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.')
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)
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
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)
174 def content_type(self, sr_uuid):
175 """Returns the content_type XML"""
176 return super(ISOSR, self).content_type(sr_uuid)
178 # pylint: disable=no-member
179 vdi_path_regex = re.compile(r"[a-z0-9.-]+\.(iso|img)", re.I)
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."""
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)
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
211 return ISOVDI(self, filename)
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')
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)
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
236 # Handle optional dconf attributes
237 self.nfsversion = nfs.validate_nfsversion(self.dconf.get('nfsversion'))
239 # Fill the required SMB version
240 self.smbversion = SMB_VERSION_3
242 # Check if smb version is specified from client
243 self.is_smbversion_specified = False
245 # Some info we need:
246 self.sr_vditype = 'phy'
248 def delete(self, sr_uuid):
249 pass
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
260 # Check whether we're already mounted
261 if self._checkmount():
262 return
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)
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 = ''
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'
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)
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')
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))
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')
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
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
410 def getSMBVersion(self):
411 """Pass smb version option to mount.cifs"""
412 smbversion = "vers=%s" % self.smbversion
413 return smbversion
415 def mountOverSMB(self, mountcmd):
416 """This function raises util.CommandException"""
417 new_env, domain = cifutils.getCIFCredentials(self.dconf, self.session,
418 prefix="cifs")
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
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')
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
451 return nfsOptions
453 def appendCIFSMountOptions(self, mountcmd):
454 """Append options to mount.cifs"""
455 options = []
456 try:
457 options.append(self.getCacheOptions())
459 if not cifutils.containsCredentials(self.dconf, prefix="cifs"):
460 options.append('guest')
462 options.append(self.getSMBVersion())
464 username, domain = (
465 cifutils.splitDomainAndUsername(self.dconf['username'])
466 )
468 if domain:
469 options.append('domain=' + domain)
470 except:
471 util.SMlog("Exception while attempting to append mount options")
472 raise
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])
479 def getCacheOptions(self):
480 """Pass cache options to mount.cifs"""
481 return "cache=none"
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
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)
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)
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)
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
511 self.other_config = self.session.xenapi.SR.get_other_config(self.sr_ref)
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.
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]
524 if latest_build_vdi is None:
525 latest_build_vdi = vdi.location
526 latest_build_number = "0"
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
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']
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()
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)
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')
574 else:
575 return super(ISOSR, self).scan(sr_uuid)
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)
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.')
594 self.detach(sr_uuid)
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
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
640 def detach(self, sr_uuid, vdi_uuid):
641 pass
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')
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()
659 if util.pathexists(self.path):
660 raise xs_errors.XenError('VDIExists')
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)
673 def delete(self, sr_uuid, vdi_uuid):
674 util.SMlog("Deleting...")
676 self.uuid = vdi_uuid
677 self._db_forget()
679 if not util.pathexists(self.path):
680 return
682 try:
683 util.SMlog("Unlinking...")
684 os.unlink(self.path)
685 util.SMlog("Done...")
686 except:
687 raise xs_errors.XenError('VDIDelete')
689 # delete, update, introduce unimplemented. super class will raise
690 # exceptions
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)