~ubuntu-branches/ubuntu/quantal/virtinst/quantal-proposed

« back to all changes in this revision

Viewing changes to .pc/9002-libvirt_disk_format.patch/virtinst/VirtualDisk.py

  • Committer: Bazaar Package Importer
  • Author(s): Marc Deslauriers
  • Date: 2011-02-01 15:40:11 UTC
  • mfrom: (1.3.16 experimental)
  • Revision ID: james.westby@ubuntu.com-20110201154011-op0nusgc240xajvb
Tags: 0.500.5-1ubuntu1
* Merge from debian experimental. Remaining changes:
  - debian/patches/9001_Ubuntu.patch:
     + Updated to add maverick and natty to OS list and enable virtio
       for them.
  - debian/patches/9003-fix-path-to-hvmloader-in-testsuite.patch: adjust
    testsuite for 0001-fix-path-to-hvmloader.patch and
    0002-Fix-path-to-pygrub.patch. (refreshed)
  - debian/control: added acl package to depends.
  - Demote virt-viewer to Suggests, as it's in universe.
  - Recommends libvirt-bin
* Removed patches:
  - debian/patches/9002-libvirt_disk_format.patch: Upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#
2
 
# Classes for building disk device xml
3
 
#
4
 
# Copyright 2006-2008  Red Hat, Inc.
5
 
# Jeremy Katz <katzj@redhat.com>
6
 
#
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.
11
 
#
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.
16
 
#
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,
20
 
# MA 02110-1301 USA.
21
 
 
22
 
import os, stat, pwd, statvfs
23
 
import subprocess
24
 
import logging
25
 
import re
26
 
 
27
 
import urlgrabber.progress as progress
28
 
import libvirt
29
 
 
30
 
import _util
31
 
import Storage
32
 
from VirtualDevice import VirtualDevice
33
 
from virtinst import _virtinst as _
34
 
 
35
 
def _vdisk_create(path, size, kind, sparse = True):
36
 
    force_fixed = "raw"
37
 
    path = os.path.expanduser(path)
38
 
    if kind in force_fixed or not sparse:
39
 
        _type = kind + ":fixed"
40
 
    else:
41
 
        _type = kind + ":sparse"
42
 
    try:
43
 
        rc = subprocess.call([ '/usr/sbin/vdiskadm', 'create', '-t', _type,
44
 
            '-s', str(size), path ])
45
 
        return rc == 0
46
 
    except OSError:
47
 
        return False
48
 
 
49
 
def _vdisk_clone(path, clone):
50
 
    logging.debug("Using vdisk clone.")
51
 
 
52
 
    path = os.path.expanduser(path)
53
 
    clone = os.path.expanduser(clone)
54
 
    try:
55
 
        rc = subprocess.call([ '/usr/sbin/vdiskadm', 'clone', path, clone ])
56
 
        return rc == 0
57
 
    except OSError:
58
 
        return False
59
 
 
60
 
def _qemu_sanitize_drvtype(phystype, fmt):
61
 
    """
62
 
    Sanitize libvirt storage volume format to a valid qemu driver type
63
 
    """
64
 
    raw_list = [ "iso" ]
65
 
 
66
 
    if phystype == VirtualDisk.TYPE_BLOCK:
67
 
        return VirtualDisk.DRIVER_QEMU_RAW
68
 
 
69
 
    if fmt in raw_list:
70
 
        return VirtualDisk.DRIVER_QEMU_RAW
71
 
 
72
 
    return fmt
73
 
 
74
 
def _name_uid(user):
75
 
    """
76
 
    Return UID for string username
77
 
    """
78
 
    pwdinfo = pwd.getpwnam(user)
79
 
    return pwdinfo[2]
80
 
 
81
 
def _is_dir_searchable(uid, username, path):
82
 
    """
83
 
    Check if passed directory is searchable by uid
84
 
    """
85
 
    try:
86
 
        statinfo = os.stat(path)
87
 
    except OSError:
88
 
        return False
89
 
 
90
 
    if uid == statinfo.st_uid:
91
 
        flag = stat.S_IXUSR
92
 
    elif uid == statinfo.st_gid:
93
 
        flag = stat.S_IXGRP
94
 
    else:
95
 
        flag = stat.S_IXOTH
96
 
 
97
 
    if bool(statinfo.st_mode & flag):
98
 
        return True
99
 
 
100
 
    # Check POSIX ACL (since that is what we use to 'fix' access)
101
 
    cmd = ["getfacl", path]
102
 
    try:
103
 
        proc = subprocess.Popen(cmd,
104
 
                                stdout=subprocess.PIPE,
105
 
                                stderr=subprocess.PIPE)
106
 
        out, err = proc.communicate()
107
 
    except OSError:
108
 
        logging.debug("Didn't find the getfacl command.")
109
 
        return False
110
 
 
111
 
    if proc.returncode != 0:
112
 
        logging.debug("Cmd '%s' failed: %s" % (cmd, err))
113
 
        return False
114
 
 
115
 
    return bool(re.search("user:%s:..x" % username, out))
116
 
 
117
 
 
118
 
class VirtualDisk(VirtualDevice):
119
 
    """
120
 
    Builds a libvirt domain disk xml description
121
 
 
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.
126
 
 
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
131
 
           as 'volInstall').
132
 
        3. An active connection ('conn') and a path to a storage volume on
133
 
           that connection.
134
 
        4. An active connection and a tuple of the form ("poolname",
135
 
           "volumename")
136
 
        5. An active connection and a path. The base of the path must
137
 
           point to the target path for an active pool.
138
 
 
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.
144
 
 
145
 
    __init__ and setting all properties performs lots of validation,
146
 
    and will throw ValueError's if problems are found.
147
 
    """
148
 
 
149
 
    _virtual_device_type = VirtualDevice.VIRTUAL_DEV_DISK
150
 
 
151
 
    DRIVER_FILE = "file"
152
 
    DRIVER_PHY = "phy"
153
 
    DRIVER_TAP = "tap"
154
 
    DRIVER_QEMU = "qemu"
155
 
    driver_names = [DRIVER_FILE, DRIVER_PHY, DRIVER_TAP, DRIVER_QEMU]
156
 
 
157
 
    DRIVER_QEMU_RAW = "raw"
158
 
    # No list here, since there are many other valid values
159
 
 
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]
166
 
 
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]
172
 
 
173
 
    DEVICE_DISK = "disk"
174
 
    DEVICE_CDROM = "cdrom"
175
 
    DEVICE_FLOPPY = "floppy"
176
 
    devices = [DEVICE_DISK, DEVICE_CDROM, DEVICE_FLOPPY]
177
 
 
178
 
    TYPE_FILE = "file"
179
 
    TYPE_BLOCK = "block"
180
 
    TYPE_DIR = "dir"
181
 
    types = [TYPE_FILE, TYPE_BLOCK, TYPE_DIR]
182
 
 
183
 
    @staticmethod
184
 
    def path_exists(conn, path):
185
 
        """
186
 
        Check if path exists. If we can't determine, return False
187
 
        """
188
 
        is_remote = _util.is_uri_remote(conn.getURI())
189
 
        try:
190
 
            vol = None
191
 
            try:
192
 
                vol = conn.storageVolLookupByPath(path)
193
 
            except:
194
 
                pass
195
 
 
196
 
            if vol:
197
 
                return True
198
 
 
199
 
            if not is_remote:
200
 
                return os.path.exists(path)
201
 
        except:
202
 
            pass
203
 
 
204
 
        return False
205
 
 
206
 
    @staticmethod
207
 
    def check_path_search_for_user(conn, path, username):
208
 
        """
209
 
        Check if the passed user has search permissions for all the
210
 
        directories in the disk path.
211
 
 
212
 
        @return: List of the directories the user cannot search, or empty list
213
 
        @rtype : C{list}
214
 
        """
215
 
        if _util.is_uri_remote(conn.getURI()):
216
 
            return []
217
 
 
218
 
        try:
219
 
            uid = _name_uid(username)
220
 
        except Exception, e:
221
 
            logging.debug("Error looking up username: %s" % str(e))
222
 
            return []
223
 
 
224
 
        fixlist = []
225
 
 
226
 
        if os.path.isdir(path):
227
 
            dirname = path
228
 
            base = "-"
229
 
        else:
230
 
            dirname, base = os.path.split(path)
231
 
 
232
 
        while base:
233
 
            if not _is_dir_searchable(uid, username, dirname):
234
 
                fixlist.append(dirname)
235
 
 
236
 
            dirname, base = os.path.split(dirname)
237
 
 
238
 
        return fixlist
239
 
 
240
 
    @staticmethod
241
 
    def fix_path_search_for_user(conn, path, username):
242
 
        """
243
 
        Try to fix any permission problems found by check_path_search_for_user
244
 
 
245
 
        @return: Return a dictionary of entries { broken path : error msg }
246
 
        @rtype : C{dict}
247
 
        """
248
 
        def fix_perms(dirname, useacl=True):
249
 
            if useacl:
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()
255
 
 
256
 
                logging.debug("Ran command '%s'" % cmd)
257
 
                if out or err:
258
 
                    logging.debug("out=%s\nerr=%s" % (out, err))
259
 
 
260
 
                if proc.returncode != 0:
261
 
                    raise ValueError(err)
262
 
            else:
263
 
                mode = os.stat(dirname).st_mode
264
 
                os.chmod(dirname, mode | stat.S_IXOTH)
265
 
 
266
 
        fixlist = VirtualDisk.check_path_search_for_user(conn, path, username)
267
 
        if not fixlist:
268
 
            return []
269
 
 
270
 
        fixlist.reverse()
271
 
        errdict = {}
272
 
 
273
 
        useacl = True
274
 
        for dirname in fixlist:
275
 
            try:
276
 
                try:
277
 
                    fix_perms(dirname, useacl)
278
 
                except:
279
 
                    # If acl fails, fall back to chmod and retry
280
 
                    if not useacl:
281
 
                        raise
282
 
                    useacl = False
283
 
 
284
 
                    fix_perms(dirname, useacl)
285
 
            except Exception, e:
286
 
                errdict[dirname] =  str(e)
287
 
 
288
 
        return errdict
289
 
 
290
 
    @staticmethod
291
 
    def path_in_use_by(conn, path, check_conflict=False):
292
 
        """
293
 
        Return a list of VM names that are using the passed path.
294
 
 
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
300
 
                               heuristics
301
 
        """
302
 
        if not path:
303
 
            return
304
 
 
305
 
        active, inactive = _util.fetch_all_guests(conn)
306
 
        vms = active + inactive
307
 
 
308
 
        def count_cb(ctx):
309
 
            c = 0
310
 
 
311
 
            template = "count(/domain/devices/disk["
312
 
            if check_conflict:
313
 
                template += "not(shareable) and "
314
 
            template += "source/@%s='%s'])"
315
 
 
316
 
            for dtype in ["dev", "file", "dir"]:
317
 
                xpath = template % (dtype, path)
318
 
                c += ctx.xpathEval(xpath)
319
 
 
320
 
            return c
321
 
 
322
 
        names = []
323
 
        for vm in vms:
324
 
            xml = vm.XMLDesc(0)
325
 
            tmpcount = _util.get_xml_path(xml, func = count_cb)
326
 
            if tmpcount:
327
 
                names.append(vm.name())
328
 
 
329
 
        return names
330
 
 
331
 
    @staticmethod
332
 
    def stat_local_path(path):
333
 
        """
334
 
        Return tuple (storage type, storage size) for the passed path on
335
 
        the local machine. This is a best effort attempt.
336
 
 
337
 
        @return: tuple of
338
 
                 (True if regular file, False otherwise, default is True,
339
 
                  max size of storage, default is 0)
340
 
        """
341
 
        try:
342
 
            return _util.stat_disk(path)
343
 
        except:
344
 
            return (True, 0)
345
 
 
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,
351
 
                 validate=True):
352
 
        """
353
 
        @param path: filesystem path to the disk image.
354
 
        @type path: C{str}
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, ...)
360
 
        @type type: C{str}
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, ...)
381
 
        @type bus: C{str}
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
389
 
        @type format: C{str}
390
 
        @param validate: Whether to validate passed parameters against the
391
 
                         local system. Omitting this may cause issues, be
392
 
                         warned!
393
 
        @type validate: C{bool}
394
 
        """
395
 
 
396
 
        VirtualDevice.__init__(self, conn=conn)
397
 
 
398
 
        self._path = None
399
 
        self._size = None
400
 
        self._type = None
401
 
        self._device = None
402
 
        self._sparse = None
403
 
        self._readOnly = None
404
 
        self._vol_object = None
405
 
        self._vol_install = None
406
 
        self._bus = None
407
 
        self._shareable = None
408
 
        self._driver_cache = None
409
 
        self._selinux_label = None
410
 
        self._clone_path = None
411
 
        self._format = None
412
 
        self._validate = validate
413
 
 
414
 
        # XXX: No property methods for these
415
 
        self.transient = transient
416
 
        self._driverName = driverName
417
 
        self._driverType = driverType
418
 
        self.target = None
419
 
 
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)
433
 
 
434
 
        if volName:
435
 
            self.__lookup_vol_name(volName)
436
 
 
437
 
        self.__validate_params()
438
 
 
439
 
 
440
 
    def __repr__(self):
441
 
        """
442
 
        prints a simple string representation for the disk instance
443
 
        """
444
 
        return "%s:%s" %(self.type, self.path)
445
 
 
446
 
 
447
 
 
448
 
    def _get_path(self):
449
 
        return self._path
450
 
    def _set_path(self, val, validate=True):
451
 
        if val is not None:
452
 
            self._check_str(val, "path")
453
 
            val = os.path.abspath(val)
454
 
 
455
 
        if validate:
456
 
            self._vol_install = None
457
 
            self._vol_object = None
458
 
            self._type = None
459
 
 
460
 
        self.__validate_wrapper("_path", val, validate)
461
 
    path = property(_get_path, _set_path)
462
 
 
463
 
    def _get_clone_path(self):
464
 
        return self._clone_path
465
 
    def _set_clone_path(self, val, validate=True):
466
 
        if val is not None:
467
 
            self._check_str(val, "path")
468
 
            val = os.path.abspath(val)
469
 
 
470
 
            # Pass the path to a VirtualDisk, which should provide validation
471
 
            # for us
472
 
            try:
473
 
                # If this disk isn't managed, don't pass 'conn' to this
474
 
                # validation disk, to ensure we have permissions for manual
475
 
                # cloning
476
 
                conn = self.__storage_specified() and self.conn or None
477
 
                VirtualDisk(conn=conn, path=val)
478
 
            except Exception, e:
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)
482
 
 
483
 
    def _get_size(self):
484
 
        return self._size
485
 
    def _set_size(self, val, validate=True):
486
 
        if val is not None:
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)
491
 
 
492
 
    def get_type(self):
493
 
        return self._type
494
 
    def set_type(self, val, validate=True):
495
 
        if val is not None:
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)
501
 
 
502
 
    def get_device(self):
503
 
        return self._device
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)
510
 
 
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)
516
 
 
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)
522
 
 
523
 
    def get_sparse(self):
524
 
        return self._sparse
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)
529
 
 
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)
536
 
 
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)
544
 
 
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 "
550
 
                                " instance.")
551
 
        self.__validate_wrapper("_vol_install", val, validate)
552
 
    vol_install = property(_get_vol_install, _set_vol_install)
553
 
 
554
 
    def _get_bus(self):
555
 
        return self._bus
556
 
    def _set_bus(self, val, validate=True):
557
 
        if val is not None:
558
 
            self._check_str(val, "bus")
559
 
        self.__validate_wrapper("_bus", val, validate)
560
 
    bus = property(_get_bus, _set_bus)
561
 
 
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)
568
 
 
569
 
    def _get_driver_cache(self):
570
 
        return self._driver_cache
571
 
    def _set_driver_cache(self, val, validate=True):
572
 
        if val is not None:
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)
578
 
 
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):
584
 
        if val is not None:
585
 
            self._check_str(val, "selinux_label")
586
 
 
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)
591
 
 
592
 
        self.__validate_wrapper("_selinux_label", val, validate)
593
 
    selinux_label = property(_get_selinux_label, _set_selinux_label)
594
 
 
595
 
    def _get_format(self):
596
 
        return self._format
597
 
    def _set_format(self, val, validate=True):
598
 
        if val is not None:
599
 
            self._check_str(val, "format")
600
 
        self.__validate_wrapper("_format", val, validate)
601
 
    format = property(_get_format, _set_format)
602
 
 
603
 
    # Validation assistance methods
604
 
 
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):
608
 
        try:
609
 
            orig = getattr(self, varname)
610
 
        except:
611
 
            orig = newval
612
 
        setattr(self, varname, newval)
613
 
        if validate:
614
 
            try:
615
 
                self.__validate_params()
616
 
            except:
617
 
                setattr(self, varname, orig)
618
 
                raise
619
 
 
620
 
    def __set_format(self):
621
 
        if not self.format:
622
 
            return
623
 
 
624
 
        if not self.__creating_storage():
625
 
            return
626
 
 
627
 
        if self.vol_install:
628
 
            if not hasattr(self.vol_install, "format"):
629
 
                raise ValueError(_("Storage type does not support format "
630
 
                                   "parameter."))
631
 
            if self.vol_install.format != self.format:
632
 
                self.vol_install.format = self.format
633
 
 
634
 
        elif self.format != "raw":
635
 
            raise RuntimeError(_("Format cannot be specified for "
636
 
                                 "unmanaged storage."))
637
 
 
638
 
    def __set_size(self):
639
 
        """
640
 
        Fill in 'size' attribute for existing storage.
641
 
        """
642
 
 
643
 
        if self.__creating_storage():
644
 
            return
645
 
 
646
 
        if self.__storage_specified() and self.vol_object:
647
 
            newsize = _util.get_xml_path(self.vol_object.XMLDesc(0),
648
 
                                         "/volume/capacity")
649
 
            try:
650
 
                newsize = float(newsize) / 1024.0 / 1024.0 / 1024.0
651
 
            except:
652
 
                newsize = 0
653
 
        elif self.path is None:
654
 
            newsize = 0
655
 
        else:
656
 
            ignore, newsize = _util.stat_disk(self.path)
657
 
            newsize = newsize / 1024.0 / 1024.0 / 1024.0
658
 
 
659
 
        if newsize != self.size:
660
 
            self._set_size(newsize, validate=False)
661
 
 
662
 
    def __set_dev_type(self):
663
 
        """
664
 
        Detect disk 'type' () from passed storage parameters
665
 
        """
666
 
 
667
 
        dtype = None
668
 
        if self.vol_object:
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
675
 
            else:
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
680
 
            else:
681
 
                dtype = self.TYPE_BLOCK
682
 
        elif self.path:
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
687
 
            else:
688
 
                dtype = self.TYPE_BLOCK
689
 
 
690
 
        if not dtype:
691
 
            dtype = self.type or self.TYPE_BLOCK
692
 
 
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)
697
 
 
698
 
    def __set_driver(self):
699
 
        """
700
 
        Set driverName and driverType from passed parameters
701
 
 
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:
705
 
 
706
 
        http://lists.gnu.org/archive/html/qemu-devel/2008-04/msg00675.html
707
 
        """
708
 
        drvname = self._driverName
709
 
        drvtype = self._driverType
710
 
 
711
 
        if self.conn:
712
 
            driver = _util.get_uri_driver(self._get_uri())
713
 
            if driver.lower() == "qemu":
714
 
                drvname = self.DRIVER_QEMU
715
 
 
716
 
        if self.vol_object:
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)
721
 
 
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)
727
 
 
728
 
        elif self.__creating_storage():
729
 
            if drvname == self.DRIVER_QEMU:
730
 
                drvtype = self.DRIVER_QEMU_RAW
731
 
 
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
736
 
 
737
 
        if self._driverName and self._driverName != drvname:
738
 
            # User already set driverName to a different value, respect that
739
 
            return
740
 
 
741
 
        self._driverName = drvname or None
742
 
        self._driverType = drvtype or None
743
 
 
744
 
    def __lookup_vol_name(self, name_tuple):
745
 
        """
746
 
        lookup volume via tuple passed via __init__'s volName parameter
747
 
        """
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')"))
752
 
        if not self.conn:
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."))
756
 
        try:
757
 
            pool = self.conn.storagePoolLookupByName(name_tuple[0])
758
 
            self._set_vol_object(pool.storageVolLookupByName(name_tuple[1]),
759
 
                                validate=False)
760
 
        except Exception, e:
761
 
            raise ValueError(_("Couldn't lookup volume object: %s" % str(e)))
762
 
 
763
 
    def __storage_specified(self):
764
 
        """
765
 
        Return bool representing if managed storage parameters have
766
 
        been explicitly specified or filled in
767
 
        """
768
 
        return (self.vol_object != None or self.vol_install != None)
769
 
 
770
 
    def __creating_storage(self):
771
 
        """
772
 
        Return True if the user requested us to create a device
773
 
        """
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)))
777
 
 
778
 
    def __no_storage(self):
779
 
        """
780
 
        Return True if no path or storage was specified
781
 
        """
782
 
        no_storage = (not self.__storage_specified() and not self.path)
783
 
        if no_storage:
784
 
            if (self.device != self.DEVICE_FLOPPY and
785
 
                self.device != self.DEVICE_CDROM):
786
 
                raise ValueError(_("Device type '%s' requires a path") %
787
 
                                 self.device)
788
 
        return no_storage
789
 
 
790
 
    def __check_if_path_managed(self):
791
 
        """
792
 
        Determine if we can use libvirt storage apis to create or lookup
793
 
        'self.path'
794
 
        """
795
 
        vol = None
796
 
        verr = None
797
 
 
798
 
        def lookup_vol_by_path():
799
 
            try:
800
 
                return self.conn.storageVolLookupByPath(self.path)
801
 
            except:
802
 
                return None
803
 
 
804
 
        pool = _util.lookup_pool_by_path(self.conn,
805
 
                                         os.path.dirname(self.path))
806
 
        vol = lookup_vol_by_path()
807
 
 
808
 
 
809
 
        # Is pool running?
810
 
        if pool and pool.info()[0] != libvirt.VIR_STORAGE_POOL_RUNNING:
811
 
            pool = None
812
 
 
813
 
        # Attempt to lookup path as a storage volume
814
 
        try:
815
 
            if vol:
816
 
                vol.info()
817
 
        except:
818
 
            try:
819
 
                try:
820
 
                    # Pool may need to be refreshed, but if it errors,
821
 
                    # invalidate it
822
 
                    if pool:
823
 
                        pool.refresh(0)
824
 
                except:
825
 
                    pool = None
826
 
                    raise
827
 
 
828
 
                vol = self.conn.storageVolLookupByPath(self.path)
829
 
                vol.info()
830
 
            except Exception, e:
831
 
                vol = None
832
 
                verr = str(e)
833
 
 
834
 
        if vol:
835
 
            self._set_vol_object(vol, validate=False)
836
 
            return
837
 
 
838
 
        if not pool:
839
 
            if not self._is_remote():
840
 
                # Building local disk
841
 
                return
842
 
 
843
 
            if not verr:
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)})
849
 
            else:
850
 
                err = (_("Cannot use storage %(path)s: %(err)s") %
851
 
                        { 'path' : self.path, 'err' : verr })
852
 
 
853
 
            raise ValueError(err)
854
 
 
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))
860
 
 
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)))
865
 
 
866
 
        volclass = Storage.StorageVolume.get_volume_for_pool(pool_object=pool)
867
 
        cap = (self.size * 1024 * 1024 * 1024)
868
 
        if self.sparse:
869
 
            alloc = 0
870
 
        else:
871
 
            alloc = cap
872
 
 
873
 
        vol = volclass(name=os.path.basename(self.path),
874
 
                       capacity=cap, allocation=alloc, pool=pool)
875
 
        self._set_vol_install(vol, validate=False)
876
 
 
877
 
 
878
 
    def __sync_params(self):
879
 
        """
880
 
        Sync some parameters between storage objects and the older
881
 
        VirtualDisk fields
882
 
        """
883
 
 
884
 
        newpath = None
885
 
        if self.vol_object:
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)
891
 
 
892
 
        if newpath and newpath != self.path:
893
 
            logging.debug("Overwriting 'path' with value from StorageVolume"
894
 
                          " object.")
895
 
            self._set_path(newpath, validate=False)
896
 
 
897
 
        if self.vol_install:
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)
903
 
 
904
 
    def _storage_security_label(self):
905
 
        """
906
 
        Return SELinux label of existing storage, or None
907
 
        """
908
 
        context = ""
909
 
 
910
 
        if self.__no_storage():
911
 
            return context
912
 
 
913
 
        if self.vol_object:
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
918
 
            # to vol_install?
919
 
            l = _util.get_xml_path(self.vol_install.pool.XMLDesc(0),
920
 
                                   "/pool/target/permissions/label")
921
 
            context = l or ""
922
 
        else:
923
 
            context = _util.selinux_getfilecon(self.path)
924
 
 
925
 
        return context
926
 
 
927
 
 
928
 
    def __validate_params(self):
929
 
        """
930
 
        function to validate all the complex interaction between the various
931
 
        disk parameters.
932
 
        """
933
 
        if not self._validate:
934
 
            return
935
 
 
936
 
        if self.__no_storage():
937
 
            # No storage specified for a removable device type (CDROM, floppy)
938
 
            self._type = self.TYPE_BLOCK
939
 
            return True
940
 
 
941
 
        storage_capable = bool(self.conn and
942
 
                               _util.is_storage_capable(self.conn))
943
 
 
944
 
        if storage_capable and not self.__storage_specified():
945
 
            # Try to lookup self.path storage objects
946
 
            self.__check_if_path_managed()
947
 
 
948
 
        if self._is_remote():
949
 
            if not storage_capable:
950
 
                raise ValueError, _("Connection doesn't support remote "
951
 
                                    "storage.")
952
 
            if not self.__storage_specified():
953
 
                raise ValueError, _("Must specify libvirt managed storage "
954
 
                                    "if on a remote connection")
955
 
 
956
 
        # Sync parameters between VirtualDisk and  storage objects.
957
 
        self.__sync_params()
958
 
 
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?
962
 
 
963
 
        managed_storage = self.__storage_specified()
964
 
        create_media = self.__creating_storage()
965
 
 
966
 
        self.__set_dev_type()
967
 
        self.__set_size()
968
 
        self.__set_format()
969
 
        self.__set_driver()
970
 
 
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
975
 
            context = ""
976
 
 
977
 
            if create_media and not managed_storage:
978
 
                context = self._expected_security_label()
979
 
            else:
980
 
                context = self._storage_security_label()
981
 
 
982
 
            self._selinux_label = context or ""
983
 
 
984
 
        # If not creating the storage, our job is easy
985
 
        if not create_media:
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)
993
 
 
994
 
            return True
995
 
 
996
 
 
997
 
        if self.device == self.DEVICE_FLOPPY or \
998
 
           self.device == self.DEVICE_CDROM:
999
 
            raise ValueError, _("Cannot create storage for %s device.") % \
1000
 
                                self.device
1001
 
 
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
1006
 
 
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 "
1013
 
                                    "'%s'" % self.path)
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)
1017
 
 
1018
 
        # Applicable for managed or local storage
1019
 
        ret = self.is_size_conflict()
1020
 
        if ret[0]:
1021
 
            raise ValueError, ret[1]
1022
 
        elif ret[1]:
1023
 
            logging.warn(ret[1])
1024
 
 
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),
1032
 
                                 validate=False)
1033
 
            # Then just leave: vol_install should handle any selinux stuff
1034
 
            return
1035
 
 
1036
 
        if self.clone_path:
1037
 
            text = (_("Cloning %(srcfile)s") %
1038
 
                    {'srcfile' : os.path.basename(self.clone_path)})
1039
 
        else:
1040
 
            text=_("Creating storage file %s") % os.path.basename(self.path)
1041
 
 
1042
 
        size_bytes = long(self.size * 1024L * 1024L * 1024L)
1043
 
        progresscb.start(filename=self.path, size=long(size_bytes),
1044
 
                         text=text)
1045
 
 
1046
 
        if self.clone_path:
1047
 
            # VDisk clone
1048
 
            if (_util.is_vdisk(self.clone_path) or
1049
 
                (os.path.exists(self.path) and _util.is_vdisk(self.path))):
1050
 
 
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"
1054
 
                                          " supported")
1055
 
                if not _vdisk_clone(self.clone_path, self.path):
1056
 
                    raise RuntimeError, _("failed to clone disk")
1057
 
                progresscb.end(size_bytes)
1058
 
 
1059
 
            else:
1060
 
                # Plain file clone
1061
 
                self._clone_local(progresscb, size_bytes)
1062
 
 
1063
 
        elif _util.is_vdisk(self.path):
1064
 
            # Create vdisk
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)
1068
 
 
1069
 
            progresscb.end(self.size)
1070
 
        else:
1071
 
            # Plain file creation
1072
 
            self._create_local_file(progresscb, size_bytes)
1073
 
 
1074
 
    def _create_local_file(self, progresscb, size_bytes):
1075
 
        """
1076
 
        Helper function which attempts to build self.path
1077
 
        """
1078
 
        fd = None
1079
 
 
1080
 
        try:
1081
 
            try:
1082
 
                fd = os.open(self.path, os.O_WRONLY | os.O_CREAT | os.O_DSYNC)
1083
 
                if self.sparse:
1084
 
                    os.ftruncate(fd, size_bytes)
1085
 
                    progresscb.update(self.size)
1086
 
                else:
1087
 
                    buf = '\x00' * 1024 * 1024 # 1 meg of nulls
1088
 
                    for i in range(0, long(self.size * 1024L)):
1089
 
                        os.write(fd, buf)
1090
 
                        progresscb.update(long(i * 1024L * 1024L))
1091
 
            except OSError, e:
1092
 
                raise RuntimeError(_("Error creating diskimage %s: %s") %
1093
 
                                   (self.path, str(e)))
1094
 
        finally:
1095
 
            if fd is not None:
1096
 
                os.close(fd)
1097
 
            progresscb.end(size_bytes)
1098
 
 
1099
 
    def _clone_local(self, meter, size_bytes):
1100
 
 
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
1105
 
            sparse = True
1106
 
            fd = None
1107
 
            try:
1108
 
                fd = os.open(self.path, os.O_WRONLY | os.O_CREAT)
1109
 
                os.ftruncate(fd, size_bytes)
1110
 
            finally:
1111
 
                if fd:
1112
 
                    os.close(fd)
1113
 
        else:
1114
 
            clone_block_size = 1024*1024*10
1115
 
            sparse = False
1116
 
 
1117
 
        logging.debug("Local Cloning %s to %s, sparse=%s, block_size=%s" %
1118
 
                      (self.clone_path, self.path, sparse, clone_block_size))
1119
 
 
1120
 
        zeros = '\0' * 4096
1121
 
 
1122
 
        src_fd, dst_fd = None, None
1123
 
        try:
1124
 
            try:
1125
 
                src_fd = os.open(self.clone_path, os.O_RDONLY)
1126
 
                dst_fd = os.open(self.path, os.O_WRONLY | os.O_CREAT)
1127
 
 
1128
 
                i=0
1129
 
                while 1:
1130
 
                    l = os.read(src_fd, clone_block_size)
1131
 
                    s = len(l)
1132
 
                    if s == 0:
1133
 
                        meter.end(size_bytes)
1134
 
                        break
1135
 
                    # check sequence of zeros
1136
 
                    if sparse and zeros == l:
1137
 
                        os.lseek(dst_fd, s, 1)
1138
 
                    else:
1139
 
                        b = os.write(dst_fd, l)
1140
 
                        if s != b:
1141
 
                            meter.end(i)
1142
 
                            break
1143
 
                    i += s
1144
 
                    if i < size_bytes:
1145
 
                        meter.update(i)
1146
 
            except OSError, e:
1147
 
                raise RuntimeError(_("Error cloning diskimage %s to %s: %s") %
1148
 
                                       (self.clone_path, self.path, str(e)))
1149
 
        finally:
1150
 
            if src_fd is not None:
1151
 
                os.close(src_fd)
1152
 
            if dst_fd is not None:
1153
 
                os.close(dst_fd)
1154
 
 
1155
 
    def setup_dev(self, conn=None, meter=None):
1156
 
        """
1157
 
        Build storage (if required)
1158
 
 
1159
 
        If storage doesn't exist (a non-existent file 'path', or 'vol_install'
1160
 
        was specified), we create it.
1161
 
 
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
1165
 
        """
1166
 
        return self.setup(meter)
1167
 
 
1168
 
    def setup(self, progresscb=None):
1169
 
        """
1170
 
        DEPRECATED: Please use setup_dev instead
1171
 
        """
1172
 
        if not progresscb:
1173
 
            progresscb = progress.BaseMeter()
1174
 
 
1175
 
        if self.__creating_storage() or self.clone_path:
1176
 
            self._do_create_storage(progresscb)
1177
 
 
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.")
1185
 
            else:
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)
1189
 
 
1190
 
    def get_xml_config(self, disknode=None):
1191
 
        """
1192
 
        @param disknode: device name in host (xvda, hdb, etc.). self.target
1193
 
                         takes precedence.
1194
 
        @type disknode: C{str}
1195
 
        """
1196
 
        typeattr = self.type
1197
 
        if self.type == VirtualDisk.TYPE_BLOCK:
1198
 
            typeattr = 'dev'
1199
 
 
1200
 
        if self.target:
1201
 
            disknode = self.target
1202
 
        if not disknode:
1203
 
            raise ValueError(_("'disknode' or self.target must be set!"))
1204
 
 
1205
 
        path = None
1206
 
        if self.vol_object:
1207
 
            path = self.vol_object.path()
1208
 
        elif self.path:
1209
 
            path = self.path
1210
 
        if path:
1211
 
            path = _util.xml_escape(path)
1212
 
 
1213
 
        ret = "    <disk type='%s' device='%s'>\n" % (self.type, self.device)
1214
 
 
1215
 
        dname = self.driver_name
1216
 
        if not dname and self.driver_cache:
1217
 
            self.driver_name = "qemu"
1218
 
 
1219
 
        if path and not self.driver_name is None:
1220
 
            dtypexml = ""
1221
 
            if not self.driver_type is None:
1222
 
                dtypexml = " type='%s'" % self.driver_type
1223
 
 
1224
 
            dcachexml = ""
1225
 
            if not self.driver_cache is None:
1226
 
                dcachexml = " cache='%s'" % self.driver_cache
1227
 
 
1228
 
            ret += "      <driver name='%s'%s%s/>\n" % (self.driver_name,
1229
 
                                                      dtypexml, dcachexml)
1230
 
 
1231
 
        if path is not None:
1232
 
            ret += "      <source %s='%s'/>\n" % (typeattr, path)
1233
 
 
1234
 
        bus_xml = ""
1235
 
        if self.bus is not None:
1236
 
            bus_xml = " bus='%s'" % self.bus
1237
 
        ret += "      <target dev='%s'%s/>\n" % (disknode, bus_xml)
1238
 
 
1239
 
        ro = self.read_only
1240
 
 
1241
 
        if self.device == self.DEVICE_CDROM:
1242
 
            ro = True
1243
 
        if self.shareable:
1244
 
            ret += "      <shareable/>\n"
1245
 
        if ro:
1246
 
            ret += "      <readonly/>\n"
1247
 
        ret += "    </disk>"
1248
 
        return ret
1249
 
 
1250
 
    def is_size_conflict(self):
1251
 
        """
1252
 
        reports if disk size conflicts with available space
1253
 
 
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")
1259
 
        """
1260
 
 
1261
 
        if self.vol_install:
1262
 
            return self.vol_install.is_size_conflict()
1263
 
 
1264
 
        if not self.__creating_storage():
1265
 
            return (False, None)
1266
 
 
1267
 
        ret = False
1268
 
        msg = 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)
1272
 
        if need > avail:
1273
 
            if self.sparse:
1274
 
                msg = _("The filesystem will not have enough free space"
1275
 
                        " to fully allocate the sparse file when the guest"
1276
 
                        " is running.")
1277
 
            else:
1278
 
                ret = True
1279
 
                msg = _("There is not enough free space to create the disk.")
1280
 
 
1281
 
 
1282
 
            if msg:
1283
 
                msg += _(" %d M requested > %d M available") % \
1284
 
                        ((need / (1024*1024)), (avail / (1024*1024)))
1285
 
        return (ret, msg)
1286
 
 
1287
 
    def is_conflict_disk(self, conn, return_names=False):
1288
 
        """
1289
 
        check if specified storage is in use by any other VMs on passed
1290
 
        connection.
1291
 
 
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}
1297
 
 
1298
 
        @return: True if a collision, False otherwise (list of names if
1299
 
                 return_names passed)
1300
 
        @rtype: C{bool}
1301
 
        """
1302
 
        if self.vol_object:
1303
 
            path = self.vol_object.path()
1304
 
        else:
1305
 
            path = self.path
1306
 
 
1307
 
        if not path:
1308
 
            return False
1309
 
 
1310
 
        if not conn:
1311
 
            conn = self.conn
1312
 
 
1313
 
        check_conflict = self.shareable
1314
 
        names = self.path_in_use_by(conn, path,
1315
 
                                    check_conflict=check_conflict)
1316
 
 
1317
 
        ret = False
1318
 
        if names:
1319
 
            ret = True
1320
 
        if return_names:
1321
 
            ret = names
1322
 
 
1323
 
        return ret
1324
 
 
1325
 
    def _support_selinux(self):
1326
 
        """
1327
 
        Return True if we have the requisite libvirt and library support
1328
 
        for selinux commands
1329
 
        """
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
1335
 
            return False
1336
 
 
1337
 
        elif self._is_remote():
1338
 
            return False
1339
 
 
1340
 
        elif not _util.have_selinux():
1341
 
            # XXX: When libvirt supports changing labels via storage APIs,
1342
 
            #      this will need changing.
1343
 
            return False
1344
 
 
1345
 
        elif self.__storage_specified() and self.path:
1346
 
            try:
1347
 
                statinfo = os.stat(self.path)
1348
 
            except:
1349
 
                return False
1350
 
 
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]
1354
 
 
1355
 
        return True
1356
 
 
1357
 
    def _expected_security_label(self):
1358
 
        """
1359
 
        Best guess at what the expected selinux label should be for the disk
1360
 
        """
1361
 
        label = None
1362
 
 
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?
1366
 
 
1367
 
        if not self._support_selinux():
1368
 
            pass
1369
 
        elif self.__no_storage():
1370
 
            pass
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()
1376
 
        else:
1377
 
            label = _util.selinux_rw_label()
1378
 
 
1379
 
        return label or ""
1380
 
 
1381
 
    def _security_can_fix(self):
1382
 
        can_fix = True
1383
 
 
1384
 
        if not self._support_selinux():
1385
 
            can_fix = False
1386
 
        elif self.__no_storage():
1387
 
            can_fix = False
1388
 
        elif self.type == VirtualDisk.TYPE_BLOCK:
1389
 
            # Shouldn't change labelling on block devices (though we can)
1390
 
            can_fix = False
1391
 
        elif not self.read_only:
1392
 
            # XXX Leave all other (R/W disk) relabeling up to libvirt/svirt
1393
 
            # for now
1394
 
            can_fix = False
1395
 
 
1396
 
        return can_fix
1397
 
 
1398
 
    def _get_target_type(self):
1399
 
        """
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
1403
 
        """
1404
 
        # The upper limits here aren't necessarilly 1024, but let the HV
1405
 
        # error as appropriate.
1406
 
        if self.bus == "virtio":
1407
 
            return ("vd", 1024)
1408
 
        elif self.bus == "scsi" or self.bus == "usb":
1409
 
            return ("sd", 1024)
1410
 
        elif self.bus == "xen":
1411
 
            return ("xvd", 1024)
1412
 
        elif self.bus == "fdc" or self.device == self.DEVICE_FLOPPY:
1413
 
            return ("fd", 2)
1414
 
        elif self.bus == "ide":
1415
 
            return ("hd", 4)
1416
 
        else:
1417
 
            return (None, None)
1418
 
 
1419
 
    def generate_target(self, skip_targets):
1420
 
        """
1421
 
        Generate target device ('hda', 'sdb', etc..) for disk, excluding
1422
 
        any targets in list 'skip_targets'. Sets self.target, and returns the
1423
 
        generated value
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
1428
 
        @rtype C{str}
1429
 
        """
1430
 
 
1431
 
        # Only use these targets if there are no other options
1432
 
        except_targets = ["hdc"]
1433
 
 
1434
 
        prefix, maxnode = self._get_target_type()
1435
 
        if prefix is None:
1436
 
            raise ValueError(_("Cannot determine device bus/type."))
1437
 
 
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:
1441
 
                self.target = "hdc"
1442
 
                return self.target
1443
 
 
1444
 
        # Regular scanning
1445
 
        for i in range(maxnode):
1446
 
            gen_t = "%s%c" % (prefix, ord('a') + i)
1447
 
            if gen_t in except_targets:
1448
 
                continue
1449
 
            if gen_t not in skip_targets:
1450
 
                self.target = gen_t
1451
 
                return self.target
1452
 
 
1453
 
        # Check except_targets for any options
1454
 
        for t in except_targets:
1455
 
            if t.startswith(prefix) and t not in skip_targets:
1456
 
                self.target = t
1457
 
                return self.target
1458
 
        raise ValueError(_("No more space for disks of type '%s'" % prefix))
1459
 
 
1460
 
 
1461
 
class XenDisk(VirtualDisk):
1462
 
    """
1463
 
    Back compat class to avoid ABI break.
1464
 
    """
1465
 
    pass