2
# Classes for building disk device xml
4
# Copyright 2006-2008 Red Hat, Inc.
5
# Jeremy Katz <katzj@redhat.com>
7
# This program is free software; you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 2 of the License, or
10
# (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
17
# You should have received a copy of the GNU General Public License
18
# along with this program; if not, write to the Free Software
19
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22
import os, stat, pwd, statvfs
27
import urlgrabber.progress as progress
32
from VirtualDevice import VirtualDevice
33
from virtinst import _virtinst as _
35
def _vdisk_create(path, size, kind, sparse = True):
37
path = os.path.expanduser(path)
38
if kind in force_fixed or not sparse:
39
_type = kind + ":fixed"
41
_type = kind + ":sparse"
43
rc = subprocess.call([ '/usr/sbin/vdiskadm', 'create', '-t', _type,
44
'-s', str(size), path ])
49
def _vdisk_clone(path, clone):
50
logging.debug("Using vdisk clone.")
52
path = os.path.expanduser(path)
53
clone = os.path.expanduser(clone)
55
rc = subprocess.call([ '/usr/sbin/vdiskadm', 'clone', path, clone ])
60
def _qemu_sanitize_drvtype(phystype, fmt):
62
Sanitize libvirt storage volume format to a valid qemu driver type
66
if phystype == VirtualDisk.TYPE_BLOCK:
67
return VirtualDisk.DRIVER_QEMU_RAW
70
return VirtualDisk.DRIVER_QEMU_RAW
76
Return UID for string username
78
pwdinfo = pwd.getpwnam(user)
81
def _is_dir_searchable(uid, username, path):
83
Check if passed directory is searchable by uid
86
statinfo = os.stat(path)
90
if uid == statinfo.st_uid:
92
elif uid == statinfo.st_gid:
97
if bool(statinfo.st_mode & flag):
100
# Check POSIX ACL (since that is what we use to 'fix' access)
101
cmd = ["getfacl", path]
103
proc = subprocess.Popen(cmd,
104
stdout=subprocess.PIPE,
105
stderr=subprocess.PIPE)
106
out, err = proc.communicate()
108
logging.debug("Didn't find the getfacl command.")
111
if proc.returncode != 0:
112
logging.debug("Cmd '%s' failed: %s" % (cmd, err))
115
return bool(re.search("user:%s:..x" % username, out))
118
class VirtualDisk(VirtualDevice):
120
Builds a libvirt domain disk xml description
122
The VirtualDisk class is used for building libvirt domain xml descriptions
123
for disk devices. If creating a disk object from an existing local block
124
device or file, a path is all that should be required. If you want to
125
create a local file, a size also needs to be specified.
127
The remote case is a bit more complex. The options are:
128
1. A libvirt virStorageVol instance (passed as 'volObject') for an
129
existing storage volume.
130
2. A virtinst L{StorageVolume} instance for creating a volume (passed
132
3. An active connection ('conn') and a path to a storage volume on
134
4. An active connection and a tuple of the form ("poolname",
136
5. An active connection and a path. The base of the path must
137
point to the target path for an active pool.
139
For cases 3 and 4, the lookup will be performed, and 'vol_object'
140
will be set to the returned virStorageVol. For the last case, 'volInstall'
141
will be populated for a StorageVolume instance. All the above cases also
142
work on a local connection as well, the only difference being that
143
option 3 won't neccessarily error out if the volume isn't found.
145
__init__ and setting all properties performs lots of validation,
146
and will throw ValueError's if problems are found.
149
_virtual_device_type = VirtualDevice.VIRTUAL_DEV_DISK
155
driver_names = [DRIVER_FILE, DRIVER_PHY, DRIVER_TAP, DRIVER_QEMU]
157
DRIVER_QEMU_RAW = "raw"
158
# No list here, since there are many other valid values
160
DRIVER_TAP_RAW = "aio"
161
DRIVER_TAP_QCOW = "qcow"
162
DRIVER_TAP_VMDK = "vmdk"
163
DRIVER_TAP_VDISK = "vdisk"
164
driver_types = [DRIVER_TAP_RAW, DRIVER_TAP_QCOW,
165
DRIVER_TAP_VMDK, DRIVER_TAP_VDISK]
167
CACHE_MODE_NONE = "none"
168
CACHE_MODE_WRITETHROUGH = "writethrough"
169
CACHE_MODE_WRITEBACK = "writeback"
170
cache_types = [CACHE_MODE_NONE, CACHE_MODE_WRITETHROUGH,
171
CACHE_MODE_WRITEBACK]
174
DEVICE_CDROM = "cdrom"
175
DEVICE_FLOPPY = "floppy"
176
devices = [DEVICE_DISK, DEVICE_CDROM, DEVICE_FLOPPY]
181
types = [TYPE_FILE, TYPE_BLOCK, TYPE_DIR]
184
def path_exists(conn, path):
186
Check if path exists. If we can't determine, return False
188
is_remote = _util.is_uri_remote(conn.getURI())
192
vol = conn.storageVolLookupByPath(path)
200
return os.path.exists(path)
207
def check_path_search_for_user(conn, path, username):
209
Check if the passed user has search permissions for all the
210
directories in the disk path.
212
@return: List of the directories the user cannot search, or empty list
215
if _util.is_uri_remote(conn.getURI()):
219
uid = _name_uid(username)
221
logging.debug("Error looking up username: %s" % str(e))
226
if os.path.isdir(path):
230
dirname, base = os.path.split(path)
233
if not _is_dir_searchable(uid, username, dirname):
234
fixlist.append(dirname)
236
dirname, base = os.path.split(dirname)
241
def fix_path_search_for_user(conn, path, username):
243
Try to fix any permission problems found by check_path_search_for_user
245
@return: Return a dictionary of entries { broken path : error msg }
248
def fix_perms(dirname, useacl=True):
250
cmd = ["setfacl", "--modify", "user:%s:x" % username, dirname]
251
proc = subprocess.Popen(cmd,
252
stdout=subprocess.PIPE,
253
stderr=subprocess.PIPE)
254
out, err = proc.communicate()
256
logging.debug("Ran command '%s'" % cmd)
258
logging.debug("out=%s\nerr=%s" % (out, err))
260
if proc.returncode != 0:
261
raise ValueError(err)
263
mode = os.stat(dirname).st_mode
264
os.chmod(dirname, mode | stat.S_IXOTH)
266
fixlist = VirtualDisk.check_path_search_for_user(conn, path, username)
274
for dirname in fixlist:
277
fix_perms(dirname, useacl)
279
# If acl fails, fall back to chmod and retry
284
fix_perms(dirname, useacl)
286
errdict[dirname] = str(e)
291
def path_in_use_by(conn, path, check_conflict=False):
293
Return a list of VM names that are using the passed path.
295
@param conn: virConnect to check VMs
296
@param path: Path to check for
297
@param check_conflict: Only return names that are truly conflicting:
298
this will omit guests that are using the disk
299
with the 'shareable' flag, and possible other
305
active, inactive = _util.fetch_all_guests(conn)
306
vms = active + inactive
311
template = "count(/domain/devices/disk["
313
template += "not(shareable) and "
314
template += "source/@%s='%s'])"
316
for dtype in ["dev", "file", "dir"]:
317
xpath = template % (dtype, path)
318
c += ctx.xpathEval(xpath)
325
tmpcount = _util.get_xml_path(xml, func = count_cb)
327
names.append(vm.name())
332
def stat_local_path(path):
334
Return tuple (storage type, storage size) for the passed path on
335
the local machine. This is a best effort attempt.
338
(True if regular file, False otherwise, default is True,
339
max size of storage, default is 0)
342
return _util.stat_disk(path)
346
def __init__(self, path=None, size=None, transient=False, type=None,
347
device=DEVICE_DISK, driverName=None, driverType=None,
348
readOnly=False, sparse=True, conn=None, volObject=None,
349
volInstall=None, volName=None, bus=None, shareable=False,
350
driverCache=None, selinuxLabel=None, format=None,
353
@param path: filesystem path to the disk image.
355
@param size: size of local file to create in gigabytes
356
@type size: C{int} or C{long} or C{float}
357
@param transient: whether to keep disk around after guest install
358
@type transient: C{bool}
359
@param type: disk media type (file, block, ...)
361
@param device: Emulated device type (disk, cdrom, floppy, ...)
362
@type device: member of devices
363
@param driverName: name of driver
364
@type driverName: member of driver_names
365
@param driverType: type of driver
366
@type driverType: member of driver_types
367
@param readOnly: Whether emulated disk is read only
368
@type readOnly: C{bool}
369
@param sparse: Create file as a sparse file
370
@type sparse: C{bool}
371
@param conn: Connection disk is being installed on
372
@type conn: libvirt.virConnect
373
@param volObject: libvirt storage volume object to use
374
@type volObject: libvirt.virStorageVol
375
@param volInstall: StorageVolume instance to build for new storage
376
@type volInstall: L{StorageVolume}
377
@param volName: Existing StorageVolume lookup information,
378
(parent pool name, volume name)
379
@type volName: C{tuple} of (C{str}, C{str})
380
@param bus: Emulated bus type (ide, scsi, virtio, ...)
382
@param shareable: If disk can be shared among VMs
383
@type shareable: C{bool}
384
@param driverCache: Disk cache mode (none, writethrough, writeback)
385
@type driverCache: member of cache_types
386
@param selinuxLabel: Used for labelling new or relabel existing storage
387
@type selinuxLabel: C{str}
388
@param format: Storage volume format to use when creating storage
390
@param validate: Whether to validate passed parameters against the
391
local system. Omitting this may cause issues, be
393
@type validate: C{bool}
396
VirtualDevice.__init__(self, conn=conn)
403
self._readOnly = None
404
self._vol_object = None
405
self._vol_install = None
407
self._shareable = None
408
self._driver_cache = None
409
self._selinux_label = None
410
self._clone_path = None
412
self._validate = validate
414
# XXX: No property methods for these
415
self.transient = transient
416
self._driverName = driverName
417
self._driverType = driverType
420
self.set_read_only(readOnly, validate=False)
421
self.set_sparse(sparse, validate=False)
422
self.set_type(type, validate=False)
423
self.set_device(device, validate=False)
424
self._set_path(path, validate=False)
425
self._set_size(size, validate=False)
426
self._set_vol_object(volObject, validate=False)
427
self._set_vol_install(volInstall, validate=False)
428
self._set_bus(bus, validate=False)
429
self._set_shareable(shareable, validate=False)
430
self._set_driver_cache(driverCache, validate=False)
431
self._set_selinux_label(selinuxLabel, validate=False)
432
self._set_format(format, validate=False)
435
self.__lookup_vol_name(volName)
437
self.__validate_params()
442
prints a simple string representation for the disk instance
444
return "%s:%s" %(self.type, self.path)
450
def _set_path(self, val, validate=True):
452
self._check_str(val, "path")
453
val = os.path.abspath(val)
456
self._vol_install = None
457
self._vol_object = None
460
self.__validate_wrapper("_path", val, validate)
461
path = property(_get_path, _set_path)
463
def _get_clone_path(self):
464
return self._clone_path
465
def _set_clone_path(self, val, validate=True):
467
self._check_str(val, "path")
468
val = os.path.abspath(val)
470
# Pass the path to a VirtualDisk, which should provide validation
473
# If this disk isn't managed, don't pass 'conn' to this
474
# validation disk, to ensure we have permissions for manual
476
conn = self.__storage_specified() and self.conn or None
477
VirtualDisk(conn=conn, path=val)
479
raise ValueError(_("Error validating clone path: %s") % e)
480
self.__validate_wrapper("_clone_path", val, validate)
481
clone_path = property(_get_clone_path, _set_clone_path)
485
def _set_size(self, val, validate=True):
487
if type(val) not in [int, float, long] or val < 0:
488
raise ValueError, _("'size' must be a number greater than 0.")
489
self.__validate_wrapper("_size", val, validate)
490
size = property(_get_size, _set_size)
494
def set_type(self, val, validate=True):
496
self._check_str(val, "type")
497
if val not in self.types:
498
raise ValueError, _("Unknown storage type '%s'" % val)
499
self.__validate_wrapper("_type", val, validate)
500
type = property(get_type, set_type)
502
def get_device(self):
504
def set_device(self, val, validate=True):
505
self._check_str(val, "device")
506
if val not in self.devices:
507
raise ValueError, _("Unknown device type '%s'" % val)
508
self.__validate_wrapper("_device", val, validate)
509
device = property(get_device, set_device)
511
def get_driver_name(self):
512
return self._driverName
513
def set_driver_name(self, val):
514
self._driverName = val
515
driver_name = property(get_driver_name, set_driver_name)
517
def get_driver_type(self):
518
return self._driverType
519
def set_driver_type(self, val):
520
self._driverType = val
521
driver_type = property(get_driver_type, set_driver_type)
523
def get_sparse(self):
525
def set_sparse(self, val, validate=True):
526
self._check_bool(val, "sparse")
527
self.__validate_wrapper("_sparse", val, validate)
528
sparse = property(get_sparse, set_sparse)
530
def get_read_only(self):
531
return self._readOnly
532
def set_read_only(self, val, validate=True):
533
self._check_bool(val, "read_only")
534
self.__validate_wrapper("_readOnly", val, validate)
535
read_only = property(get_read_only, set_read_only)
537
def _get_vol_object(self):
538
return self._vol_object
539
def _set_vol_object(self, val, validate=True):
540
if val is not None and not isinstance(val, libvirt.virStorageVol):
541
raise ValueError, _("vol_object must be a virStorageVol instance")
542
self.__validate_wrapper("_vol_object", val, validate)
543
vol_object = property(_get_vol_object, _set_vol_object)
545
def _get_vol_install(self):
546
return self._vol_install
547
def _set_vol_install(self, val, validate=True):
548
if val is not None and not isinstance(val, Storage.StorageVolume):
549
raise ValueError, _("vol_install must be a StorageVolume "
551
self.__validate_wrapper("_vol_install", val, validate)
552
vol_install = property(_get_vol_install, _set_vol_install)
556
def _set_bus(self, val, validate=True):
558
self._check_str(val, "bus")
559
self.__validate_wrapper("_bus", val, validate)
560
bus = property(_get_bus, _set_bus)
562
def _get_shareable(self):
563
return self._shareable
564
def _set_shareable(self, val, validate=True):
565
self._check_bool(val, "shareable")
566
self.__validate_wrapper("_shareable", val, validate)
567
shareable = property(_get_shareable, _set_shareable)
569
def _get_driver_cache(self):
570
return self._driver_cache
571
def _set_driver_cache(self, val, validate=True):
573
self._check_str(val, "cache")
574
if val not in self.cache_types:
575
raise ValueError, _("Unknown cache mode '%s'" % val)
576
self.__validate_wrapper("_driver_cache", val, validate)
577
driver_cache = property(_get_driver_cache, _set_driver_cache)
579
# If there is no selinux support on the libvirt connection or the
580
# system, we won't throw errors if this is set, just silently ignore.
581
def _get_selinux_label(self):
582
return self._selinux_label
583
def _set_selinux_label(self, val, validate=True):
585
self._check_str(val, "selinux_label")
587
if (self._support_selinux() and
588
not _util.selinux_is_label_valid(val)):
589
# XXX Not valid if we support changing labels remotely
590
raise ValueError(_("SELinux label '%s' is not valid.") % val)
592
self.__validate_wrapper("_selinux_label", val, validate)
593
selinux_label = property(_get_selinux_label, _set_selinux_label)
595
def _get_format(self):
597
def _set_format(self, val, validate=True):
599
self._check_str(val, "format")
600
self.__validate_wrapper("_format", val, validate)
601
format = property(_get_format, _set_format)
603
# Validation assistance methods
605
# Initializes attribute if it hasn't been done, then validates args.
606
# If validation fails, reset attribute to original value and raise error
607
def __validate_wrapper(self, varname, newval, validate=True):
609
orig = getattr(self, varname)
612
setattr(self, varname, newval)
615
self.__validate_params()
617
setattr(self, varname, orig)
620
def __set_format(self):
624
if not self.__creating_storage():
628
if not hasattr(self.vol_install, "format"):
629
raise ValueError(_("Storage type does not support format "
631
if self.vol_install.format != self.format:
632
self.vol_install.format = self.format
634
elif self.format != "raw":
635
raise RuntimeError(_("Format cannot be specified for "
636
"unmanaged storage."))
638
def __set_size(self):
640
Fill in 'size' attribute for existing storage.
643
if self.__creating_storage():
646
if self.__storage_specified() and self.vol_object:
647
newsize = _util.get_xml_path(self.vol_object.XMLDesc(0),
650
newsize = float(newsize) / 1024.0 / 1024.0 / 1024.0
653
elif self.path is None:
656
ignore, newsize = _util.stat_disk(self.path)
657
newsize = newsize / 1024.0 / 1024.0 / 1024.0
659
if newsize != self.size:
660
self._set_size(newsize, validate=False)
662
def __set_dev_type(self):
664
Detect disk 'type' () from passed storage parameters
669
# vol info is [ vol type (file or block), capacity, allocation ]
670
t = self.vol_object.info()[0]
671
if t == libvirt.VIR_STORAGE_VOL_FILE:
672
dtype = self.TYPE_FILE
673
elif t == libvirt.VIR_STORAGE_VOL_BLOCK:
674
dtype = self.TYPE_BLOCK
676
raise ValueError, _("Unknown storage volume type.")
677
elif self.vol_install:
678
if self.vol_install.file_type == libvirt.VIR_STORAGE_VOL_FILE:
679
dtype = self.TYPE_FILE
681
dtype = self.TYPE_BLOCK
683
if os.path.isdir(self.path):
684
dtype = self.TYPE_DIR
685
elif _util.stat_disk(self.path)[0]:
686
dtype = self.TYPE_FILE
688
dtype = self.TYPE_BLOCK
691
dtype = self.type or self.TYPE_BLOCK
693
elif self.type and dtype != self.type:
694
raise ValueError(_("Passed type '%s' does not match detected "
695
"storage type '%s'" % (self.type, dtype)))
696
self.set_type(dtype, validate=False)
698
def __set_driver(self):
700
Set driverName and driverType from passed parameters
702
Where possible, we want to force driverName = "raw" if installing
703
a QEMU VM. Without telling QEMU to expect a raw file, the emulator
704
is forced to autodetect, which has security implications:
706
http://lists.gnu.org/archive/html/qemu-devel/2008-04/msg00675.html
708
drvname = self._driverName
709
drvtype = self._driverType
712
driver = _util.get_uri_driver(self._get_uri())
713
if driver.lower() == "qemu":
714
drvname = self.DRIVER_QEMU
717
fmt = _util.get_xml_path(self.vol_object.XMLDesc(0),
718
"/volume/target/format/@type")
719
if drvname == self.DRIVER_QEMU:
720
drvtype = _qemu_sanitize_drvtype(self.type, fmt)
722
elif self.vol_install:
723
if drvname == self.DRIVER_QEMU:
724
if hasattr(self.vol_install, "format"):
725
drvtype = _qemu_sanitize_drvtype(self.type,
726
self.vol_install.format)
728
elif self.__creating_storage():
729
if drvname == self.DRIVER_QEMU:
730
drvtype = self.DRIVER_QEMU_RAW
732
elif self.path and os.path.exists(self.path):
733
if _util.is_vdisk(self.path):
734
drvname = self.DRIVER_TAP
735
drvtype = self.DRIVER_TAP_VDISK
737
if self._driverName and self._driverName != drvname:
738
# User already set driverName to a different value, respect that
741
self._driverName = drvname or None
742
self._driverType = drvtype or None
744
def __lookup_vol_name(self, name_tuple):
746
lookup volume via tuple passed via __init__'s volName parameter
748
if type(name_tuple) is not tuple or len(name_tuple) != 2 \
749
or (type(name_tuple[0]) is not type(name_tuple[1]) is not str):
750
raise ValueError(_("volName must be a tuple of the form "
751
"('poolname', 'volname')"))
753
raise ValueError(_("'volName' requires a passed connection."))
754
if not _util.is_storage_capable(self.conn):
755
raise ValueError(_("Connection does not support storage lookup."))
757
pool = self.conn.storagePoolLookupByName(name_tuple[0])
758
self._set_vol_object(pool.storageVolLookupByName(name_tuple[1]),
761
raise ValueError(_("Couldn't lookup volume object: %s" % str(e)))
763
def __storage_specified(self):
765
Return bool representing if managed storage parameters have
766
been explicitly specified or filled in
768
return (self.vol_object != None or self.vol_install != None)
770
def __creating_storage(self):
772
Return True if the user requested us to create a device
774
return not (self.__no_storage() or
775
(self.__storage_specified() and self.vol_object) or
776
(self.path and os.path.exists(self.path)))
778
def __no_storage(self):
780
Return True if no path or storage was specified
782
no_storage = (not self.__storage_specified() and not self.path)
784
if (self.device != self.DEVICE_FLOPPY and
785
self.device != self.DEVICE_CDROM):
786
raise ValueError(_("Device type '%s' requires a path") %
790
def __check_if_path_managed(self):
792
Determine if we can use libvirt storage apis to create or lookup
798
def lookup_vol_by_path():
800
return self.conn.storageVolLookupByPath(self.path)
804
pool = _util.lookup_pool_by_path(self.conn,
805
os.path.dirname(self.path))
806
vol = lookup_vol_by_path()
810
if pool and pool.info()[0] != libvirt.VIR_STORAGE_POOL_RUNNING:
813
# Attempt to lookup path as a storage volume
820
# Pool may need to be refreshed, but if it errors,
828
vol = self.conn.storageVolLookupByPath(self.path)
835
self._set_vol_object(vol, validate=False)
839
if not self._is_remote():
840
# Building local disk
844
# Since there is no error, no pool was ever found
845
err = (_("Cannot use storage '%(path)s': '%(rootdir)s' is "
846
"not managed on the remote host.") %
847
{ 'path' : self.path,
848
'rootdir' : os.path.dirname(self.path)})
850
err = (_("Cannot use storage %(path)s: %(err)s") %
851
{ 'path' : self.path, 'err' : verr })
853
raise ValueError(err)
855
# Path wasn't a volume. See if base of path is a managed
856
# pool, and if so, setup a StorageVolume object
857
if self.size == None:
858
raise ValueError(_("Size must be specified for non "
859
"existent volume path '%s'" % self.path))
861
logging.debug("Path '%s' is target for pool '%s'. "
862
"Creating volume '%s'." %
863
(os.path.dirname(self.path), pool.name(),
864
os.path.basename(self.path)))
866
volclass = Storage.StorageVolume.get_volume_for_pool(pool_object=pool)
867
cap = (self.size * 1024 * 1024 * 1024)
873
vol = volclass(name=os.path.basename(self.path),
874
capacity=cap, allocation=alloc, pool=pool)
875
self._set_vol_install(vol, validate=False)
878
def __sync_params(self):
880
Sync some parameters between storage objects and the older
886
newpath = self.vol_object.path()
887
elif self.vol_install:
888
newpath = (_util.get_xml_path(self.vol_install.pool.XMLDesc(0),
889
"/pool/target/path") + "/" +
890
self.vol_install.name)
892
if newpath and newpath != self.path:
893
logging.debug("Overwriting 'path' with value from StorageVolume"
895
self._set_path(newpath, validate=False)
898
newsize = self.vol_install.capacity/1024.0/1024.0/1024.0
899
if self.size != newsize:
900
logging.debug("Overwriting 'size' with value from "
901
"StorageVolume object")
902
self._set_size(newsize, validate=False)
904
def _storage_security_label(self):
906
Return SELinux label of existing storage, or None
910
if self.__no_storage():
914
context = _util.get_xml_path(self.vol_object.XMLDesc(0),
915
"/volume/target/permissions/label")
916
elif self.vol_install:
917
# XXX: If user entered a manual label, should we sync this
919
l = _util.get_xml_path(self.vol_install.pool.XMLDesc(0),
920
"/pool/target/permissions/label")
923
context = _util.selinux_getfilecon(self.path)
928
def __validate_params(self):
930
function to validate all the complex interaction between the various
933
if not self._validate:
936
if self.__no_storage():
937
# No storage specified for a removable device type (CDROM, floppy)
938
self._type = self.TYPE_BLOCK
941
storage_capable = bool(self.conn and
942
_util.is_storage_capable(self.conn))
944
if storage_capable and not self.__storage_specified():
945
# Try to lookup self.path storage objects
946
self.__check_if_path_managed()
948
if self._is_remote():
949
if not storage_capable:
950
raise ValueError, _("Connection doesn't support remote "
952
if not self.__storage_specified():
953
raise ValueError, _("Must specify libvirt managed storage "
954
"if on a remote connection")
956
# Sync parameters between VirtualDisk and storage objects.
959
# The main distinctions from this point forward:
960
# - Are we doing storage API operations or local media checks?
961
# - Do we need to create the storage?
963
managed_storage = self.__storage_specified()
964
create_media = self.__creating_storage()
966
self.__set_dev_type()
971
if not self.selinux_label:
972
# If we are using existing storage, pull the label from it
973
# If we are installing via vol_install, pull from the parent pool
974
# If we are creating local storage, use the expected label
977
if create_media and not managed_storage:
978
context = self._expected_security_label()
980
context = self._storage_security_label()
982
self._selinux_label = context or ""
984
# If not creating the storage, our job is easy
986
# Make sure we have access to the local path
987
if not managed_storage:
988
if (os.path.isdir(self.path) and
989
not _util.is_vdisk(self.path) and
990
not self.device == self.DEVICE_FLOPPY):
991
raise ValueError(_("The path '%s' must be a file or a "
992
"device, not a directory") % self.path)
997
if self.device == self.DEVICE_FLOPPY or \
998
self.device == self.DEVICE_CDROM:
999
raise ValueError, _("Cannot create storage for %s device.") % \
1002
if not managed_storage:
1003
if self.type is self.TYPE_BLOCK:
1004
raise ValueError, _("Local block device path '%s' must "
1005
"exist.") % self.path
1007
# Path doesn't exist: make sure we have write access to dir
1008
if not os.access(os.path.dirname(self.path), os.R_OK):
1009
raise ValueError("No read access to directory '%s'" %
1010
os.path.dirname(self.path))
1011
if self.size is None:
1012
raise ValueError, _("size is required for non-existent disk "
1014
if not os.access(os.path.dirname(self.path), os.W_OK):
1015
raise ValueError, _("No write access to directory '%s'") % \
1016
os.path.dirname(self.path)
1018
# Applicable for managed or local storage
1019
ret = self.is_size_conflict()
1021
raise ValueError, ret[1]
1023
logging.warn(ret[1])
1025
# Storage creation routines
1026
def _do_create_storage(self, progresscb):
1027
# If a clone_path is specified, but not vol_install.input_vol,
1028
# that means we are cloning unmanaged -> managed, so skip this
1029
if (self.vol_install and
1030
(not self.clone_path or self.vol_install.input_vol)):
1031
self._set_vol_object(self.vol_install.install(meter=progresscb),
1033
# Then just leave: vol_install should handle any selinux stuff
1037
text = (_("Cloning %(srcfile)s") %
1038
{'srcfile' : os.path.basename(self.clone_path)})
1040
text=_("Creating storage file %s") % os.path.basename(self.path)
1042
size_bytes = long(self.size * 1024L * 1024L * 1024L)
1043
progresscb.start(filename=self.path, size=long(size_bytes),
1048
if (_util.is_vdisk(self.clone_path) or
1049
(os.path.exists(self.path) and _util.is_vdisk(self.path))):
1051
if (not _util.is_vdisk(self.clone_path) or
1052
os.path.exists(self.path)):
1053
raise RuntimeError, _("copying to an existing vdisk is not"
1055
if not _vdisk_clone(self.clone_path, self.path):
1056
raise RuntimeError, _("failed to clone disk")
1057
progresscb.end(size_bytes)
1061
self._clone_local(progresscb, size_bytes)
1063
elif _util.is_vdisk(self.path):
1065
progresscb.update(1024)
1066
if not _vdisk_create(self.path, size_bytes, "vmdk", self.sparse):
1067
raise RuntimeError, _("Error creating vdisk %s" % self.path)
1069
progresscb.end(self.size)
1071
# Plain file creation
1072
self._create_local_file(progresscb, size_bytes)
1074
def _create_local_file(self, progresscb, size_bytes):
1076
Helper function which attempts to build self.path
1082
fd = os.open(self.path, os.O_WRONLY | os.O_CREAT | os.O_DSYNC)
1084
os.ftruncate(fd, size_bytes)
1085
progresscb.update(self.size)
1087
buf = '\x00' * 1024 * 1024 # 1 meg of nulls
1088
for i in range(0, long(self.size * 1024L)):
1090
progresscb.update(long(i * 1024L * 1024L))
1092
raise RuntimeError(_("Error creating diskimage %s: %s") %
1093
(self.path, str(e)))
1097
progresscb.end(size_bytes)
1099
def _clone_local(self, meter, size_bytes):
1101
# if a destination file exists and sparse flg is True,
1102
# this priority takes a existing file.
1103
if (os.path.exists(self.path) == False and self.sparse == True):
1104
clone_block_size = 4096
1108
fd = os.open(self.path, os.O_WRONLY | os.O_CREAT)
1109
os.ftruncate(fd, size_bytes)
1114
clone_block_size = 1024*1024*10
1117
logging.debug("Local Cloning %s to %s, sparse=%s, block_size=%s" %
1118
(self.clone_path, self.path, sparse, clone_block_size))
1122
src_fd, dst_fd = None, None
1125
src_fd = os.open(self.clone_path, os.O_RDONLY)
1126
dst_fd = os.open(self.path, os.O_WRONLY | os.O_CREAT)
1130
l = os.read(src_fd, clone_block_size)
1133
meter.end(size_bytes)
1135
# check sequence of zeros
1136
if sparse and zeros == l:
1137
os.lseek(dst_fd, s, 1)
1139
b = os.write(dst_fd, l)
1147
raise RuntimeError(_("Error cloning diskimage %s to %s: %s") %
1148
(self.clone_path, self.path, str(e)))
1150
if src_fd is not None:
1152
if dst_fd is not None:
1155
def setup_dev(self, conn=None, meter=None):
1157
Build storage (if required)
1159
If storage doesn't exist (a non-existent file 'path', or 'vol_install'
1160
was specified), we create it.
1162
@param conn: Optional connection to use if self.conn not specified
1163
@param meter: Progress meter to report file creation on
1164
@type meter: instanceof urlgrabber.BaseMeter
1166
return self.setup(meter)
1168
def setup(self, progresscb=None):
1170
DEPRECATED: Please use setup_dev instead
1173
progresscb = progress.BaseMeter()
1175
if self.__creating_storage() or self.clone_path:
1176
self._do_create_storage(progresscb)
1178
# Relabel storage if it was requested
1179
storage_label = self._storage_security_label()
1180
if storage_label and storage_label != self.selinux_label:
1181
if not self._support_selinux():
1182
logging.debug("No support for changing selinux context.")
1183
elif not self._security_can_fix():
1184
logging.debug("Can't fix selinux context in this case.")
1186
logging.debug("Changing path=%s selinux label %s -> %s" %
1187
(self.path, storage_label, self.selinux_label))
1188
_util.selinux_setfilecon(self.path, self.selinux_label)
1190
def get_xml_config(self, disknode=None):
1192
@param disknode: device name in host (xvda, hdb, etc.). self.target
1194
@type disknode: C{str}
1196
typeattr = self.type
1197
if self.type == VirtualDisk.TYPE_BLOCK:
1201
disknode = self.target
1203
raise ValueError(_("'disknode' or self.target must be set!"))
1207
path = self.vol_object.path()
1211
path = _util.xml_escape(path)
1213
ret = " <disk type='%s' device='%s'>\n" % (self.type, self.device)
1215
dname = self.driver_name
1216
if not dname and self.driver_cache:
1217
self.driver_name = "qemu"
1219
if path and not self.driver_name is None:
1221
if not self.driver_type is None:
1222
dtypexml = " type='%s'" % self.driver_type
1225
if not self.driver_cache is None:
1226
dcachexml = " cache='%s'" % self.driver_cache
1228
ret += " <driver name='%s'%s%s/>\n" % (self.driver_name,
1229
dtypexml, dcachexml)
1231
if path is not None:
1232
ret += " <source %s='%s'/>\n" % (typeattr, path)
1235
if self.bus is not None:
1236
bus_xml = " bus='%s'" % self.bus
1237
ret += " <target dev='%s'%s/>\n" % (disknode, bus_xml)
1241
if self.device == self.DEVICE_CDROM:
1244
ret += " <shareable/>\n"
1246
ret += " <readonly/>\n"
1250
def is_size_conflict(self):
1252
reports if disk size conflicts with available space
1254
returns a two element tuple:
1255
1. first element is True if fatal conflict occurs
1256
2. second element is a string description of the conflict or None
1257
Non fatal conflicts (sparse disk exceeds available space) will
1258
return (False, "description of collision")
1261
if self.vol_install:
1262
return self.vol_install.is_size_conflict()
1264
if not self.__creating_storage():
1265
return (False, None)
1269
vfs = os.statvfs(os.path.dirname(self.path))
1270
avail = vfs[statvfs.F_FRSIZE] * vfs[statvfs.F_BAVAIL]
1271
need = long(self.size * 1024L * 1024L * 1024L)
1274
msg = _("The filesystem will not have enough free space"
1275
" to fully allocate the sparse file when the guest"
1279
msg = _("There is not enough free space to create the disk.")
1283
msg += _(" %d M requested > %d M available") % \
1284
((need / (1024*1024)), (avail / (1024*1024)))
1287
def is_conflict_disk(self, conn, return_names=False):
1289
check if specified storage is in use by any other VMs on passed
1292
@param conn: connection to check for collisions on
1293
@type conn: libvirt.virConnect
1294
@param return_names: Whether or not to return a list of VM names using
1295
the same storage (default = False)
1296
@type return_names: C{bool}
1298
@return: True if a collision, False otherwise (list of names if
1299
return_names passed)
1303
path = self.vol_object.path()
1313
check_conflict = self.shareable
1314
names = self.path_in_use_by(conn, path,
1315
check_conflict=check_conflict)
1325
def _support_selinux(self):
1327
Return True if we have the requisite libvirt and library support
1328
for selinux commands
1330
if (not self._caps and False):
1331
#self._caps.host.secmodel is None or
1332
#self._caps.host.secmodel.model != "selinux"):
1333
# XXX: Libvirt support isn't strictly required, but all the
1334
# our label guesses are built with svirt in mind
1337
elif self._is_remote():
1340
elif not _util.have_selinux():
1341
# XXX: When libvirt supports changing labels via storage APIs,
1342
# this will need changing.
1345
elif self.__storage_specified() and self.path:
1347
statinfo = os.stat(self.path)
1351
# Not sure if this is even the correct metric for
1352
# 'Can we change the file context'
1353
return os.geteuid() in ['0', statinfo.st_uid]
1357
def _expected_security_label(self):
1359
Best guess at what the expected selinux label should be for the disk
1363
# XXX: These are really only approximations in the remote case?
1364
# XXX: Maybe libvirt should expose the relevant selinux labels in
1365
# the capabilities XML?
1367
if not self._support_selinux():
1369
elif self.__no_storage():
1371
elif self.read_only:
1372
label = _util.selinux_readonly_label()
1373
elif self.shareable:
1374
# XXX: Should this be different? or do we not care about MLS here?
1375
label = _util.selinux_rw_label()
1377
label = _util.selinux_rw_label()
1381
def _security_can_fix(self):
1384
if not self._support_selinux():
1386
elif self.__no_storage():
1388
elif self.type == VirtualDisk.TYPE_BLOCK:
1389
# Shouldn't change labelling on block devices (though we can)
1391
elif not self.read_only:
1392
# XXX Leave all other (R/W disk) relabeling up to libvirt/svirt
1398
def _get_target_type(self):
1400
Returns the suggested disk target prefix (hd, xvd, sd ...) from
1401
the passed parameters.
1402
@returns: str prefix, or None if no reasonable guess can be made
1404
# The upper limits here aren't necessarilly 1024, but let the HV
1405
# error as appropriate.
1406
if self.bus == "virtio":
1408
elif self.bus == "scsi" or self.bus == "usb":
1410
elif self.bus == "xen":
1411
return ("xvd", 1024)
1412
elif self.bus == "fdc" or self.device == self.DEVICE_FLOPPY:
1414
elif self.bus == "ide":
1419
def generate_target(self, skip_targets):
1421
Generate target device ('hda', 'sdb', etc..) for disk, excluding
1422
any targets in list 'skip_targets'. Sets self.target, and returns the
1424
@param used_targets: list of targets to exclude
1425
@type used_targets: C{list}
1426
@raise ValueError: can't determine target type, no targets available
1427
@returns generated target
1431
# Only use these targets if there are no other options
1432
except_targets = ["hdc"]
1434
prefix, maxnode = self._get_target_type()
1436
raise ValueError(_("Cannot determine device bus/type."))
1438
# Special case: IDE cdrom should prefer hdc for back compat
1439
if self.device == self.DEVICE_CDROM and prefix == "hd":
1440
if "hdc" not in skip_targets:
1445
for i in range(maxnode):
1446
gen_t = "%s%c" % (prefix, ord('a') + i)
1447
if gen_t in except_targets:
1449
if gen_t not in skip_targets:
1453
# Check except_targets for any options
1454
for t in except_targets:
1455
if t.startswith(prefix) and t not in skip_targets:
1458
raise ValueError(_("No more space for disks of type '%s'" % prefix))
1461
class XenDisk(VirtualDisk):
1463
Back compat class to avoid ABI break.