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

« back to all changes in this revision

Viewing changes to virtinst/VirtualDisk.py

  • Committer: Bazaar Package Importer
  • Author(s): Laurent Léonard
  • Date: 2011-01-29 21:41:21 UTC
  • mto: (1.6.3 sid)
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: james.westby@ubuntu.com-20110129214121-pjuxf2xz08l5zqew
Tags: upstream-0.500.5
Import upstream version 0.500.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20
20
# MA 02110-1301 USA.
21
21
 
22
 
import os, stat, pwd, statvfs
 
22
import os
 
23
import stat
 
24
import pwd
 
25
import statvfs
23
26
import subprocess
24
27
import logging
25
28
import re
30
33
import _util
31
34
import Storage
32
35
from VirtualDevice import VirtualDevice
 
36
from XMLBuilderDomain import _xml_property
33
37
from virtinst import _virtinst as _
34
38
 
35
 
def _vdisk_create(path, size, kind, sparse = True):
 
39
def _vdisk_create(path, size, kind, sparse=True):
36
40
    force_fixed = "raw"
37
41
    path = os.path.expanduser(path)
38
42
    if kind in force_fixed or not sparse:
57
61
    except OSError:
58
62
        return False
59
63
 
60
 
def _qemu_sanitize_drvtype(phystype, fmt):
 
64
def _qemu_sanitize_drvtype(phystype, fmt, manual_format=False):
61
65
    """
62
66
    Sanitize libvirt storage volume format to a valid qemu driver type
63
67
    """
64
68
    raw_list = [ "iso" ]
65
69
 
66
 
    if phystype == VirtualDisk.TYPE_BLOCK and not fmt:
67
 
        return VirtualDisk.DRIVER_QEMU_RAW
 
70
    if phystype == VirtualDisk.TYPE_BLOCK:
 
71
        if not fmt:
 
72
            return VirtualDisk.DRIVER_QEMU_RAW
 
73
        if fmt and not manual_format:
 
74
            return VirtualDisk.DRIVER_QEMU_RAW
68
75
 
69
76
    if fmt in raw_list:
70
77
        return VirtualDisk.DRIVER_QEMU_RAW
114
121
 
115
122
    return bool(re.search("user:%s:..x" % username, out))
116
123
 
 
124
def _check_if_pool_source(conn, path):
 
125
    """
 
126
    If passed path is a host disk device like /dev/sda, want to let the user
 
127
    use it
 
128
    """
 
129
    if not _util.is_storage_capable(conn):
 
130
        return None
 
131
 
 
132
    def check_pool(poolname, path):
 
133
        pool = conn.storagePoolLookupByName(poolname)
 
134
        xml = pool.XMLDesc(0)
 
135
 
 
136
        for element in ["dir", "device", "adapter"]:
 
137
            xml_path = _util.get_xml_path(xml,
 
138
                                          "/pool/source/%s/@path" % element)
 
139
            if xml_path == path:
 
140
                return pool
 
141
 
 
142
    running_list = conn.listStoragePools()
 
143
    inactive_list = conn.listDefinedStoragePools()
 
144
    for plist in [running_list, inactive_list]:
 
145
        for name in plist:
 
146
            p = check_pool(name, path)
 
147
            if p:
 
148
                return p
 
149
    return None
 
150
 
 
151
def _check_if_path_managed(conn, path):
 
152
    """
 
153
    Determine if we can use libvirt storage APIs to create or lookup
 
154
    the passed path. If we can't, throw an error
 
155
    """
 
156
    vol = None
 
157
    verr = None
 
158
    path_is_pool = False
 
159
 
 
160
    def lookup_vol_by_path():
 
161
        try:
 
162
            vol = conn.storageVolLookupByPath(path)
 
163
            vol.info()
 
164
            return vol, None
 
165
        except Exception, e:
 
166
            return None, e
 
167
 
 
168
    pool = _util.lookup_pool_by_path(conn,
 
169
                                     os.path.dirname(path))
 
170
    vol = lookup_vol_by_path()[0]
 
171
 
 
172
    # Is pool running?
 
173
    if pool and pool.info()[0] != libvirt.VIR_STORAGE_POOL_RUNNING:
 
174
        pool = None
 
175
 
 
176
    # Attempt to lookup path as a storage volume
 
177
    if pool and not vol:
 
178
        try:
 
179
            # Pool may need to be refreshed, but if it errors,
 
180
            # invalidate it
 
181
            if pool:
 
182
                pool.refresh(0)
 
183
 
 
184
            vol, verr = lookup_vol_by_path()
 
185
        except Exception, e:
 
186
            vol = None
 
187
            pool = None
 
188
            verr = str(e)
 
189
 
 
190
    if not (vol or pool):
 
191
        # See if path is a pool source, and allow it through
 
192
        pool = _check_if_pool_source(conn, path)
 
193
        path_is_pool = bool(pool)
 
194
 
 
195
    if not vol and not pool:
 
196
        if not _util.is_uri_remote(conn.getURI()):
 
197
            # Building local disk
 
198
            return None, None, False
 
199
 
 
200
        if not verr:
 
201
            # Since there is no error, no pool was ever found
 
202
            err = (_("Cannot use storage '%(path)s': '%(rootdir)s' is "
 
203
                     "not managed on the remote host.") %
 
204
                      { 'path' : path,
 
205
                        'rootdir' : os.path.dirname(path)})
 
206
        else:
 
207
            err = (_("Cannot use storage %(path)s: %(err)s") %
 
208
                    { 'path' : path, 'err' : verr })
 
209
 
 
210
        raise ValueError(err)
 
211
 
 
212
    return vol, pool, path_is_pool
 
213
 
 
214
def _build_vol_install(path, pool, size, sparse):
 
215
    # Path wasn't a volume. See if base of path is a managed
 
216
    # pool, and if so, setup a StorageVolume object
 
217
    if size == None:
 
218
        raise ValueError(_("Size must be specified for non "
 
219
                           "existent volume path '%s'" % path))
 
220
 
 
221
    logging.debug("Path '%s' is target for pool '%s'. "
 
222
                  "Creating volume '%s'." %
 
223
                  (os.path.dirname(path), pool.name(),
 
224
                   os.path.basename(path)))
 
225
 
 
226
    volclass = Storage.StorageVolume.get_volume_for_pool(pool_object=pool)
 
227
    cap = (size * 1024 * 1024 * 1024)
 
228
    if sparse:
 
229
        alloc = 0
 
230
    else:
 
231
        alloc = cap
 
232
 
 
233
    volinst = volclass(name=os.path.basename(path),
 
234
                       capacity=cap, allocation=alloc, pool=pool)
 
235
    return volinst
 
236
 
 
237
 
 
238
def _lookup_vol_name(conn, name_tuple):
 
239
    """
 
240
    lookup volume via tuple passed via __init__'s volName parameter
 
241
    """
 
242
    if type(name_tuple) is not tuple or len(name_tuple) != 2 \
 
243
        or (type(name_tuple[0]) is not type(name_tuple[1]) is not str):
 
244
        raise ValueError(_("volName must be a tuple of the form "
 
245
                           "('poolname', 'volname')"))
 
246
 
 
247
    if not conn:
 
248
        raise ValueError(_("'volName' requires a passed connection."))
 
249
    if not _util.is_storage_capable(conn):
 
250
        raise ValueError(_("Connection does not support storage lookup."))
 
251
 
 
252
    try:
 
253
        pool = conn.storagePoolLookupByName(name_tuple[0])
 
254
        return pool.storageVolLookupByName(name_tuple[1])
 
255
    except Exception, e:
 
256
        raise ValueError(_("Couldn't lookup volume object: %s" % str(e)))
117
257
 
118
258
class VirtualDisk(VirtualDevice):
119
259
    """
180
320
    TYPE_DIR = "dir"
181
321
    types = [TYPE_FILE, TYPE_BLOCK, TYPE_DIR]
182
322
 
 
323
    _target_props = ["file", "dev", "dir"]
 
324
 
 
325
    @staticmethod
 
326
    def disk_type_to_xen_driver_name(disk_type):
 
327
        """
 
328
        Convert a value of VirtualDisk.type to it's associated Xen
 
329
        <driver name=/> property
 
330
        """
 
331
        if disk_type == VirtualDisk.TYPE_BLOCK:
 
332
            return "phy"
 
333
        elif disk_type == VirtualDisk.TYPE_FILE:
 
334
            return "file"
 
335
        return "file"
 
336
 
 
337
    @staticmethod
 
338
    def disk_type_to_target_prop(disk_type):
 
339
        """
 
340
        Convert a value of VirtualDisk.type to it's associated XML
 
341
        target property name
 
342
        """
 
343
        if disk_type == VirtualDisk.TYPE_FILE:
 
344
            return "file"
 
345
        elif disk_type == VirtualDisk.TYPE_BLOCK:
 
346
            return "dev"
 
347
        elif disk_type == VirtualDisk.TYPE_DIR:
 
348
            return "dir"
 
349
        return "file"
 
350
 
183
351
    @staticmethod
184
352
    def path_exists(conn, path):
185
353
        """
188
356
        is_remote = _util.is_uri_remote(conn.getURI())
189
357
        try:
190
358
            vol = None
 
359
            path_is_pool = False
191
360
            try:
192
 
                vol = conn.storageVolLookupByPath(path)
 
361
                vol, ignore, path_is_pool = _check_if_path_managed(conn, path)
193
362
            except:
194
363
                pass
195
364
 
196
 
            if vol:
 
365
            if vol or path_is_pool:
197
366
                return True
198
367
 
199
368
            if not is_remote:
283
452
 
284
453
                    fix_perms(dirname, useacl)
285
454
            except Exception, e:
286
 
                errdict[dirname] =  str(e)
 
455
                errdict[dirname] = str(e)
287
456
 
288
457
        return errdict
289
458
 
313
482
                template += "not(shareable) and "
314
483
            template += "source/@%s='%s'])"
315
484
 
316
 
            for dtype in ["dev", "file", "dir"]:
 
485
            for dtype in VirtualDisk._target_props:
317
486
                xpath = template % (dtype, path)
318
487
                c += ctx.xpathEval(xpath)
319
488
 
322
491
        names = []
323
492
        for vm in vms:
324
493
            xml = vm.XMLDesc(0)
325
 
            tmpcount = _util.get_xml_path(xml, func = count_cb)
 
494
            tmpcount = _util.get_xml_path(xml, func=count_cb)
326
495
            if tmpcount:
327
496
                names.append(vm.name())
328
497
 
348
517
                 readOnly=False, sparse=True, conn=None, volObject=None,
349
518
                 volInstall=None, volName=None, bus=None, shareable=False,
350
519
                 driverCache=None, selinuxLabel=None, format=None,
351
 
                 validate=True):
 
520
                 validate=True, parsexml=None, parsexmlnode=None, caps=None):
352
521
        """
353
522
        @param path: filesystem path to the disk image.
354
523
        @type path: C{str}
393
562
        @type validate: C{bool}
394
563
        """
395
564
 
396
 
        VirtualDevice.__init__(self, conn=conn)
 
565
        VirtualDevice.__init__(self, conn=conn,
 
566
                               parsexml=parsexml, parsexmlnode=parsexmlnode,
 
567
                               caps=caps)
397
568
 
398
569
        self._path = None
399
570
        self._size = None
402
573
        self._sparse = None
403
574
        self._readOnly = None
404
575
        self._vol_object = None
 
576
        self._pool_object = None
405
577
        self._vol_install = None
406
578
        self._bus = None
407
579
        self._shareable = None
409
581
        self._selinux_label = None
410
582
        self._clone_path = None
411
583
        self._format = None
 
584
        self._driverName = driverName
 
585
        self._driverType = driverType
 
586
        self._target = None
412
587
        self._validate = validate
413
588
 
414
589
        # XXX: No property methods for these
415
590
        self.transient = transient
416
 
        self._driverName = driverName
417
 
        self._driverType = driverType
418
 
        self.target = None
 
591
 
 
592
        if volName and not volObject:
 
593
            volObject = _lookup_vol_name(conn, volName)
 
594
 
 
595
        if self._is_parse():
 
596
            self._validate = False
 
597
            return
419
598
 
420
599
        self.set_read_only(readOnly, validate=False)
421
600
        self.set_sparse(sparse, validate=False)
431
610
        self._set_selinux_label(selinuxLabel, validate=False)
432
611
        self._set_format(format, validate=False)
433
612
 
434
 
        if volName:
435
 
            self.__lookup_vol_name(volName)
436
 
 
 
613
        self.__change_storage(self.path,
 
614
                              self.vol_object,
 
615
                              self.vol_install)
437
616
        self.__validate_params()
438
617
 
439
618
 
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
 
 
 
619
    #
 
620
    # Parameters for specifying the backing storage
 
621
    #
447
622
 
448
623
    def _get_path(self):
449
 
        return self._path
 
624
        retpath = self._path
 
625
        if self.vol_object:
 
626
            retpath = self.vol_object.path()
 
627
        elif self.vol_install:
 
628
            retpath = (_util.get_xml_path(self.vol_install.pool.XMLDesc(0),
 
629
                                          "/pool/target/path") + "/" +
 
630
                       self.vol_install.name)
 
631
 
 
632
        return retpath
450
633
    def _set_path(self, val, validate=True):
451
634
        if val is not None:
452
635
            self._check_str(val, "path")
453
636
            val = os.path.abspath(val)
454
637
 
455
638
        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
 
 
 
639
            self.__change_storage(path=val)
 
640
        self.__validate_wrapper("_path", val, validate, self.path)
 
641
    def _xml_get_xpath(self):
 
642
        xpath = None
 
643
        for prop in self._target_props:
 
644
            xpath = "./source/@" + prop
 
645
            if self._xml_ctx.xpathEval(xpath):
 
646
                return xpath
 
647
        return "./source/@file"
 
648
    def _xml_set_xpath(self):
 
649
        return "./source/@" + self.disk_type_to_target_prop(self.type)
 
650
    path = _xml_property(_get_path, _set_path,
 
651
                         xml_get_xpath=_xml_get_xpath,
 
652
                         xml_set_xpath=_xml_set_xpath,)
 
653
 
 
654
 
 
655
    def _get_vol_object(self):
 
656
        return self._vol_object
 
657
    def _set_vol_object(self, val, validate=True):
 
658
        if val is not None and not isinstance(val, libvirt.virStorageVol):
 
659
            raise ValueError(_("vol_object must be a virStorageVol instance"))
 
660
 
 
661
        if validate:
 
662
            self.__change_storage(vol_object=val)
 
663
        self.__validate_wrapper("_vol_object", val, validate, self.vol_object)
 
664
    vol_object = property(_get_vol_object, _set_vol_object)
 
665
 
 
666
    def _get_vol_install(self):
 
667
        return self._vol_install
 
668
    def _set_vol_install(self, val, validate=True):
 
669
        if val is not None and not isinstance(val, Storage.StorageVolume):
 
670
            raise ValueError(_("vol_install must be a StorageVolume "
 
671
                               " instance."))
 
672
 
 
673
        if validate:
 
674
            self.__change_storage(vol_install=val)
 
675
        self.__validate_wrapper("_vol_install", val, validate, self.vol_install)
 
676
    vol_install = property(_get_vol_install, _set_vol_install)
 
677
 
 
678
    #
 
679
    # Other properties
 
680
    #
463
681
    def _get_clone_path(self):
464
682
        return self._clone_path
465
683
    def _set_clone_path(self, val, validate=True):
473
691
                # If this disk isn't managed, don't pass 'conn' to this
474
692
                # validation disk, to ensure we have permissions for manual
475
693
                # cloning
476
 
                conn = self.__storage_specified() and self.conn or None
 
694
                conn = self.__managed_storage() and self.conn or None
477
695
                VirtualDisk(conn=conn, path=val)
478
696
            except Exception, e:
479
697
                raise ValueError(_("Error validating clone path: %s") % e)
480
 
        self.__validate_wrapper("_clone_path", val, validate)
 
698
        self.__validate_wrapper("_clone_path", val, validate, self.clone_path)
481
699
    clone_path = property(_get_clone_path, _set_clone_path)
482
700
 
483
701
    def _get_size(self):
484
 
        return self._size
 
702
        retsize = self.__existing_storage_size()
 
703
        if retsize is None:
 
704
            if self.vol_install:
 
705
                retsize = self.vol_install.capacity / 1024.0 / 1024.0 / 1024.0
 
706
            else:
 
707
                retsize = self._size
 
708
 
 
709
        return retsize
485
710
    def _set_size(self, val, validate=True):
486
711
        if val is not None:
487
712
            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)
 
713
                raise ValueError(_("'size' must be a number greater than 0."))
 
714
 
 
715
        self.__validate_wrapper("_size", val, validate, self.size)
490
716
    size = property(_get_size, _set_size)
491
717
 
492
718
    def get_type(self):
493
 
        return self._type
 
719
        if self._type:
 
720
            return self._type
 
721
        return self.__existing_storage_dev_type()
494
722
    def set_type(self, val, validate=True):
495
723
        if val is not None:
496
724
            self._check_str(val, "type")
497
725
            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)
 
726
                raise ValueError(_("Unknown storage type '%s'" % val))
 
727
        self.__validate_wrapper("_type", val, validate, self.type)
 
728
    type = _xml_property(get_type, set_type,
 
729
                         xpath="./@type")
501
730
 
502
731
    def get_device(self):
503
732
        return self._device
504
733
    def set_device(self, val, validate=True):
505
734
        self._check_str(val, "device")
506
735
        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)
 
736
            raise ValueError(_("Unknown device type '%s'" % val))
 
737
 
 
738
        if val == self._device:
 
739
            return
 
740
 
 
741
        if self._is_parse():
 
742
            self.bus = None
 
743
            self.target = None
 
744
        self.__validate_wrapper("_device", val, validate, self.device)
 
745
    device = _xml_property(get_device, set_device,
 
746
                           xpath="./@device")
510
747
 
511
748
    def get_driver_name(self):
512
 
        return self._driverName
513
 
    def set_driver_name(self, val):
 
749
        retname = self._driverName
 
750
        if not retname:
 
751
            retname, ignore = self.__get_default_driver()
 
752
        return retname
 
753
    def set_driver_name(self, val, validate=True):
 
754
        ignore = validate
514
755
        self._driverName = val
515
 
    driver_name = property(get_driver_name, set_driver_name)
 
756
    driver_name = _xml_property(get_driver_name, set_driver_name,
 
757
                                xpath="./driver/@name")
516
758
 
517
759
    def get_driver_type(self):
518
 
        return self._driverType
519
 
    def set_driver_type(self, val):
 
760
        rettype = self._driverType
 
761
        if not rettype:
 
762
            ignore, rettype = self.__get_default_driver()
 
763
        return rettype
 
764
    def set_driver_type(self, val, validate=True):
 
765
        ignore = validate
520
766
        self._driverType = val
521
 
    driver_type = property(get_driver_type, set_driver_type)
 
767
    driver_type = _xml_property(get_driver_type, set_driver_type,
 
768
                                xpath="./driver/@type")
522
769
 
523
770
    def get_sparse(self):
524
771
        return self._sparse
525
772
    def set_sparse(self, val, validate=True):
526
773
        self._check_bool(val, "sparse")
527
 
        self.__validate_wrapper("_sparse", val, validate)
 
774
        self.__validate_wrapper("_sparse", val, validate, self.sparse)
528
775
    sparse = property(get_sparse, set_sparse)
529
776
 
530
777
    def get_read_only(self):
531
778
        return self._readOnly
532
779
    def set_read_only(self, val, validate=True):
533
780
        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)
 
781
        self.__validate_wrapper("_readOnly", val, validate, self.read_only)
 
782
    read_only = _xml_property(get_read_only, set_read_only,
 
783
                              xpath="./readonly", is_bool=True)
553
784
 
554
785
    def _get_bus(self):
555
786
        return self._bus
556
787
    def _set_bus(self, val, validate=True):
557
788
        if val is not None:
558
789
            self._check_str(val, "bus")
559
 
        self.__validate_wrapper("_bus", val, validate)
560
 
    bus = property(_get_bus, _set_bus)
 
790
        self.__validate_wrapper("_bus", val, validate, self.bus)
 
791
    bus = _xml_property(_get_bus, _set_bus,
 
792
                        xpath="./target/@bus")
 
793
    def _get_target(self):
 
794
        return self._target
 
795
    def _set_target(self, val, validate=True):
 
796
        ignore = validate
 
797
        if val is not None:
 
798
            self._check_str(val, "target")
 
799
        self._target = val
 
800
    target = _xml_property(_get_target, _set_target,
 
801
                           xpath="./target/@dev")
561
802
 
562
803
    def _get_shareable(self):
563
804
        return self._shareable
564
805
    def _set_shareable(self, val, validate=True):
565
806
        self._check_bool(val, "shareable")
566
 
        self.__validate_wrapper("_shareable", val, validate)
567
 
    shareable = property(_get_shareable, _set_shareable)
 
807
        self.__validate_wrapper("_shareable", val, validate, self.shareable)
 
808
    shareable = _xml_property(_get_shareable, _set_shareable,
 
809
                              xpath="./shareable", is_bool=True)
568
810
 
569
811
    def _get_driver_cache(self):
570
812
        return self._driver_cache
572
814
        if val is not None:
573
815
            self._check_str(val, "cache")
574
816
            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)
 
817
                raise ValueError(_("Unknown cache mode '%s'" % val))
 
818
        self.__validate_wrapper("_driver_cache", val, validate,
 
819
                                self.driver_cache)
 
820
    driver_cache = _xml_property(_get_driver_cache, _set_driver_cache,
 
821
                                 xpath="./driver/@cache")
578
822
 
579
823
    # If there is no selinux support on the libvirt connection or the
580
824
    # system, we won't throw errors if this is set, just silently ignore.
581
825
    def _get_selinux_label(self):
582
 
        return self._selinux_label
 
826
        # If selinux_label manually specified, return it
 
827
        # If we are using existing storage, pull the label from it
 
828
        # If we are installing via vol_install, pull from the parent pool
 
829
        # If we are creating local storage, use the expected label
 
830
        retlabel = self._selinux_label
 
831
        if not retlabel:
 
832
            retlabel = ""
 
833
            if self.__creating_storage() and not self.__managed_storage():
 
834
                retlabel = self._expected_security_label()
 
835
            else:
 
836
                retlabel = self._storage_security_label()
 
837
 
 
838
        return retlabel
583
839
    def _set_selinux_label(self, val, validate=True):
584
840
        if val is not None:
585
841
            self._check_str(val, "selinux_label")
589
845
                # XXX Not valid if we support changing labels remotely
590
846
                raise ValueError(_("SELinux label '%s' is not valid.") % val)
591
847
 
592
 
        self.__validate_wrapper("_selinux_label", val, validate)
 
848
        self.__validate_wrapper("_selinux_label", val, validate,
 
849
                                self.selinux_label)
593
850
    selinux_label = property(_get_selinux_label, _set_selinux_label)
594
851
 
595
852
    def _get_format(self):
597
854
    def _set_format(self, val, validate=True):
598
855
        if val is not None:
599
856
            self._check_str(val, "format")
600
 
        self.__validate_wrapper("_format", val, validate)
 
857
        self.__validate_wrapper("_format", val, validate, self.format)
601
858
    format = property(_get_format, _set_format)
602
859
 
603
860
    # Validation assistance methods
604
861
 
605
862
    # Initializes attribute if it hasn't been done, then validates args.
606
863
    # 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
 
864
    def __validate_wrapper(self, varname, newval, validate, origval):
 
865
        orig = origval
612
866
        setattr(self, varname, newval)
 
867
 
613
868
        if validate:
614
869
            try:
615
870
                self.__validate_params()
617
872
                setattr(self, varname, orig)
618
873
                raise
619
874
 
 
875
 
 
876
    def __change_storage(self, path=None, vol_object=None, vol_install=None):
 
877
        """
 
878
        Validates and updates params when the backing storage is changed
 
879
        """
 
880
        pool = None
 
881
 
 
882
        storage_capable = bool(self.conn and
 
883
                               _util.is_storage_capable(self.conn))
 
884
 
 
885
        # Try to lookup self.path storage objects
 
886
        if vol_object or vol_install:
 
887
            pass
 
888
        elif not storage_capable:
 
889
            pass
 
890
        elif path:
 
891
            vol_object, pool, path_is_pool = _check_if_path_managed(self.conn,
 
892
                                                                    path)
 
893
            if pool and not vol_object and not path_is_pool:
 
894
                vol_install = _build_vol_install(path, pool,
 
895
                                                 self.size,
 
896
                                                 self.sparse)
 
897
 
 
898
            if not path_is_pool:
 
899
                pool = None
 
900
 
 
901
        # Finally, set the relevant params
 
902
        self._set_path(path, validate=False)
 
903
        self._set_vol_object(vol_object, validate=False)
 
904
        self._set_vol_install(vol_install, validate=False)
 
905
        self._pool_object = pool
 
906
 
 
907
        # XXX: Hack, we shouldn't have to conditionalize for parsing
 
908
        if self._is_parse():
 
909
            self.type = self.get_type()
 
910
            self.driver_name = self.get_driver_name()
 
911
            self.driver_type = self.get_driver_type()
 
912
 
 
913
 
620
914
    def __set_format(self):
621
915
        if not self.format:
622
916
            return
635
929
            raise RuntimeError(_("Format cannot be specified for "
636
930
                                 "unmanaged storage."))
637
931
 
638
 
    def __set_size(self):
639
 
        """
640
 
        Fill in 'size' attribute for existing storage.
641
 
        """
642
 
 
 
932
    def __existing_storage_size(self):
 
933
        """
 
934
        Return size of existing storage
 
935
        """
643
936
        if self.__creating_storage():
644
937
            return
645
938
 
646
 
        if self.__storage_specified() and self.vol_object:
 
939
        if self.vol_object:
647
940
            newsize = _util.get_xml_path(self.vol_object.XMLDesc(0),
648
941
                                         "/volume/capacity")
649
942
            try:
650
943
                newsize = float(newsize) / 1024.0 / 1024.0 / 1024.0
651
944
            except:
652
945
                newsize = 0
 
946
        elif self._pool_object:
 
947
            newsize = _util.get_xml_path(self.vol_object.XMLDesc(0),
 
948
                                         "/pool/capacity")
 
949
            try:
 
950
                newsize = float(newsize) / 1024.0 / 1024.0 / 1024.0
 
951
            except:
 
952
                newsize = 0
653
953
        elif self.path is None:
654
954
            newsize = 0
655
955
        else:
656
956
            ignore, newsize = _util.stat_disk(self.path)
657
957
            newsize = newsize / 1024.0 / 1024.0 / 1024.0
658
958
 
659
 
        if newsize != self.size:
660
 
            self._set_size(newsize, validate=False)
 
959
        return newsize
661
960
 
662
 
    def __set_dev_type(self):
 
961
    def __existing_storage_dev_type(self):
663
962
        """
664
963
        Detect disk 'type' () from passed storage parameters
665
964
        """
673
972
            elif t == libvirt.VIR_STORAGE_VOL_BLOCK:
674
973
                dtype = self.TYPE_BLOCK
675
974
            else:
676
 
                raise ValueError, _("Unknown storage volume type.")
 
975
                dtype = self.TYPE_FILE
 
976
 
677
977
        elif self.vol_install:
678
978
            if self.vol_install.file_type == libvirt.VIR_STORAGE_VOL_FILE:
679
979
                dtype = self.TYPE_FILE
680
980
            else:
681
981
                dtype = self.TYPE_BLOCK
 
982
        elif self._pool_object:
 
983
            xml = self._pool_object.XMLDesc(0)
 
984
            for source, source_type in [("dir", self.TYPE_DIR),
 
985
                                        ("device", self.TYPE_BLOCK),
 
986
                                        ("adapter", self.TYPE_BLOCK)]:
 
987
                if _util.get_xml_path(xml, "/pool/source/%s/@dev" % source):
 
988
                    dtype = source_type
 
989
                    break
 
990
 
682
991
        elif self.path:
683
992
            if os.path.isdir(self.path):
684
993
                dtype = self.TYPE_DIR
688
997
                dtype = self.TYPE_BLOCK
689
998
 
690
999
        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):
 
1000
            dtype = self._type or self.TYPE_BLOCK
 
1001
 
 
1002
        return dtype
 
1003
 
 
1004
    def __get_default_driver(self):
699
1005
        """
700
1006
        Set driverName and driverType from passed parameters
701
1007
 
709
1015
        drvtype = self._driverType
710
1016
 
711
1017
        if self.conn:
712
 
            driver = _util.get_uri_driver(self._get_uri())
713
 
            if driver.lower() == "qemu":
 
1018
            is_qemu = _util.is_qemu(self.conn)
 
1019
            if is_qemu and not drvname:
714
1020
                drvname = self.DRIVER_QEMU
715
1021
 
716
1022
        if self.format:
717
1023
            if drvname == self.DRIVER_QEMU:
718
 
                drvtype = _qemu_sanitize_drvtype(self.type, self.format)
 
1024
                drvtype = _qemu_sanitize_drvtype(self.type, self.format,
 
1025
                                                 manual_format=True)
719
1026
 
720
1027
        elif self.vol_object:
721
1028
            fmt = _util.get_xml_path(self.vol_object.XMLDesc(0),
738
1045
                drvname = self.DRIVER_TAP
739
1046
                drvtype = self.DRIVER_TAP_VDISK
740
1047
 
741
 
        # User already set driverName to a different value, respect that
742
 
        if self._driverName and self._driverName != drvname:
743
 
            return
744
 
        self._driverName = drvname or None
745
 
 
746
 
        if self._driverType and self._driverType != drvtype:
747
 
            return
748
 
        self._driverType = drvtype or None
749
 
 
750
 
    def __lookup_vol_name(self, name_tuple):
751
 
        """
752
 
        lookup volume via tuple passed via __init__'s volName parameter
753
 
        """
754
 
        if type(name_tuple) is not tuple or len(name_tuple) != 2 \
755
 
           or (type(name_tuple[0]) is not type(name_tuple[1]) is not str):
756
 
            raise ValueError(_("volName must be a tuple of the form "
757
 
                               "('poolname', 'volname')"))
758
 
        if not self.conn:
759
 
            raise ValueError(_("'volName' requires a passed connection."))
760
 
        if not _util.is_storage_capable(self.conn):
761
 
            raise ValueError(_("Connection does not support storage lookup."))
762
 
        try:
763
 
            pool = self.conn.storagePoolLookupByName(name_tuple[0])
764
 
            self._set_vol_object(pool.storageVolLookupByName(name_tuple[1]),
765
 
                                validate=False)
766
 
        except Exception, e:
767
 
            raise ValueError(_("Couldn't lookup volume object: %s" % str(e)))
768
 
 
769
 
    def __storage_specified(self):
 
1048
        return drvname or None, drvtype or None
 
1049
 
 
1050
    def __managed_storage(self):
770
1051
        """
771
1052
        Return bool representing if managed storage parameters have
772
1053
        been explicitly specified or filled in
773
1054
        """
774
 
        return (self.vol_object != None or self.vol_install != None)
 
1055
        return (self.vol_object != None or self.vol_install != None or
 
1056
                self._pool_object != None)
775
1057
 
776
1058
    def __creating_storage(self):
777
1059
        """
778
1060
        Return True if the user requested us to create a device
779
1061
        """
780
1062
        return not (self.__no_storage() or
781
 
                    (self.__storage_specified() and self.vol_object) or
 
1063
                    (self.__managed_storage() and
 
1064
                     self.vol_object or self._pool_object) or
782
1065
                    (self.path and os.path.exists(self.path)))
783
1066
 
784
1067
    def __no_storage(self):
785
1068
        """
786
1069
        Return True if no path or storage was specified
787
1070
        """
788
 
        no_storage = (not self.__storage_specified() and not self.path)
789
 
        if no_storage:
790
 
            if (self.device != self.DEVICE_FLOPPY and
791
 
                self.device != self.DEVICE_CDROM):
792
 
                raise ValueError(_("Device type '%s' requires a path") %
793
 
                                 self.device)
794
 
        return no_storage
795
 
 
796
 
    def __check_if_path_managed(self):
797
 
        """
798
 
        Determine if we can use libvirt storage apis to create or lookup
799
 
        'self.path'
800
 
        """
801
 
        vol = None
802
 
        verr = None
803
 
 
804
 
        def lookup_vol_by_path():
805
 
            try:
806
 
                vol = self.conn.storageVolLookupByPath(self.path)
807
 
                vol.info()
808
 
                return vol, None
809
 
            except Exception, e:
810
 
                return None, e
811
 
 
812
 
        pool = _util.lookup_pool_by_path(self.conn,
813
 
                                         os.path.dirname(self.path))
814
 
        vol = lookup_vol_by_path()[0]
815
 
 
816
 
 
817
 
        # Is pool running?
818
 
        if pool and pool.info()[0] != libvirt.VIR_STORAGE_POOL_RUNNING:
819
 
            pool = None
820
 
 
821
 
        # Attempt to lookup path as a storage volume
822
 
        if pool and not vol:
823
 
            try:
824
 
                # Pool may need to be refreshed, but if it errors,
825
 
                # invalidate it
826
 
                if pool:
827
 
                    pool.refresh(0)
828
 
 
829
 
                vol, verr = lookup_vol_by_path()
830
 
            except Exception, e:
831
 
                vol = None
832
 
                pool = None
833
 
                verr = str(e)
834
 
 
835
 
        if vol:
836
 
            self._set_vol_object(vol, validate=False)
837
 
            return
838
 
 
839
 
        if not pool:
840
 
            if not self._is_remote():
841
 
                # Building local disk
842
 
                return
843
 
 
844
 
            if not verr:
845
 
                # Since there is no error, no pool was ever found
846
 
                err = (_("Cannot use storage '%(path)s': '%(rootdir)s' is "
847
 
                         "not managed on the remote host.") %
848
 
                         { 'path' : self.path,
849
 
                           'rootdir' : os.path.dirname(self.path)})
850
 
            else:
851
 
                err = (_("Cannot use storage %(path)s: %(err)s") %
852
 
                        { 'path' : self.path, 'err' : verr })
853
 
 
854
 
            raise ValueError(err)
855
 
 
856
 
        # Path wasn't a volume. See if base of path is a managed
857
 
        # pool, and if so, setup a StorageVolume object
858
 
        if self.size == None:
859
 
            raise ValueError(_("Size must be specified for non "
860
 
                               "existent volume path '%s'" % self.path))
861
 
 
862
 
        logging.debug("Path '%s' is target for pool '%s'. "
863
 
                      "Creating volume '%s'." %
864
 
                      (os.path.dirname(self.path), pool.name(),
865
 
                       os.path.basename(self.path)))
866
 
 
867
 
        volclass = Storage.StorageVolume.get_volume_for_pool(pool_object=pool)
868
 
        cap = (self.size * 1024 * 1024 * 1024)
869
 
        if self.sparse:
870
 
            alloc = 0
871
 
        else:
872
 
            alloc = cap
873
 
 
874
 
        vol = volclass(name=os.path.basename(self.path),
875
 
                       capacity=cap, allocation=alloc, pool=pool)
876
 
        self._set_vol_install(vol, validate=False)
877
 
 
878
 
 
879
 
    def __sync_params(self):
880
 
        """
881
 
        Sync some parameters between storage objects and the older
882
 
        VirtualDisk fields
883
 
        """
884
 
 
885
 
        newpath = None
886
 
        if self.vol_object:
887
 
            newpath = self.vol_object.path()
888
 
        elif self.vol_install:
889
 
            newpath = (_util.get_xml_path(self.vol_install.pool.XMLDesc(0),
890
 
                                         "/pool/target/path") + "/" +
891
 
                       self.vol_install.name)
892
 
 
893
 
        if newpath and newpath != self.path:
894
 
            logging.debug("Overwriting 'path' with value from StorageVolume"
895
 
                          " object.")
896
 
            self._set_path(newpath, validate=False)
897
 
 
898
 
        if self.vol_install:
899
 
            newsize = self.vol_install.capacity/1024.0/1024.0/1024.0
900
 
            if self.size != newsize:
901
 
                logging.debug("Overwriting 'size' with value from "
902
 
                              "StorageVolume object")
903
 
                self._set_size(newsize, validate=False)
 
1071
        return (not self.__managed_storage() and not self.path)
 
1072
 
904
1073
 
905
1074
    def _storage_security_label(self):
906
1075
        """
914
1083
        if self.vol_object:
915
1084
            context = _util.get_xml_path(self.vol_object.XMLDesc(0),
916
1085
                                         "/volume/target/permissions/label")
 
1086
        elif self._pool_object:
 
1087
            context = _util.get_xml_path(self._pool_object.XMLDesc(0),
 
1088
                                         "/pool/target/permissions/label")
917
1089
        elif self.vol_install:
918
1090
            # XXX: If user entered a manual label, should we sync this
919
1091
            # to vol_install?
934
1106
        if not self._validate:
935
1107
            return
936
1108
 
 
1109
        # No storage specified for a removable device type (CDROM, floppy)
937
1110
        if self.__no_storage():
938
 
            # No storage specified for a removable device type (CDROM, floppy)
939
 
            self._type = self.TYPE_BLOCK
 
1111
            if (self.device != self.DEVICE_FLOPPY and
 
1112
                self.device != self.DEVICE_CDROM):
 
1113
                raise ValueError(_("Device type '%s' requires a path") %
 
1114
                                 self.device)
 
1115
 
940
1116
            return True
941
1117
 
942
1118
        storage_capable = bool(self.conn and
943
1119
                               _util.is_storage_capable(self.conn))
944
1120
 
945
 
        if storage_capable and not self.__storage_specified():
946
 
            # Try to lookup self.path storage objects
947
 
            self.__check_if_path_managed()
948
 
 
949
1121
        if self._is_remote():
950
1122
            if not storage_capable:
951
 
                raise ValueError, _("Connection doesn't support remote "
952
 
                                    "storage.")
953
 
            if not self.__storage_specified():
954
 
                raise ValueError, _("Must specify libvirt managed storage "
955
 
                                    "if on a remote connection")
956
 
 
957
 
        # Sync parameters between VirtualDisk and  storage objects.
958
 
        self.__sync_params()
 
1123
                raise ValueError(_("Connection doesn't support remote "
 
1124
                                   "storage."))
 
1125
            if not self.__managed_storage():
 
1126
                raise ValueError(_("Must specify libvirt managed storage "
 
1127
                                   "if on a remote connection"))
959
1128
 
960
1129
        # The main distinctions from this point forward:
961
1130
        # - Are we doing storage API operations or local media checks?
962
1131
        # - Do we need to create the storage?
963
1132
 
964
 
        managed_storage = self.__storage_specified()
 
1133
        managed_storage = self.__managed_storage()
965
1134
        create_media = self.__creating_storage()
966
1135
 
967
 
        self.__set_dev_type()
968
 
        self.__set_size()
969
1136
        self.__set_format()
970
 
        self.__set_driver()
971
 
 
972
 
        if not self.selinux_label:
973
 
            # If we are using existing storage, pull the label from it
974
 
            # If we are installing via vol_install, pull from the parent pool
975
 
            # If we are creating local storage, use the expected label
976
 
            context = ""
977
 
 
978
 
            if create_media and not managed_storage:
979
 
                context = self._expected_security_label()
980
 
            else:
981
 
                context = self._storage_security_label()
982
 
 
983
 
            self._selinux_label = context or ""
984
1137
 
985
1138
        # If not creating the storage, our job is easy
986
1139
        if not create_media:
997
1150
 
998
1151
        if self.device == self.DEVICE_FLOPPY or \
999
1152
           self.device == self.DEVICE_CDROM:
1000
 
            raise ValueError, _("Cannot create storage for %s device.") % \
1001
 
                                self.device
 
1153
            raise ValueError(_("Cannot create storage for %s device.") %
 
1154
                               self.device)
1002
1155
 
1003
1156
        if not managed_storage:
1004
1157
            if self.type is self.TYPE_BLOCK:
1005
 
                raise ValueError, _("Local block device path '%s' must "
1006
 
                                    "exist.") % self.path
 
1158
                raise ValueError(_("Local block device path '%s' must "
 
1159
                                   "exist.") % self.path)
1007
1160
 
1008
1161
            # Path doesn't exist: make sure we have write access to dir
1009
1162
            if not os.access(os.path.dirname(self.path), os.R_OK):
1010
1163
                raise ValueError("No read access to directory '%s'" %
1011
1164
                                 os.path.dirname(self.path))
1012
1165
            if self.size is None:
1013
 
                raise ValueError, _("size is required for non-existent disk "
1014
 
                                    "'%s'" % self.path)
 
1166
                raise ValueError(_("size is required for non-existent disk "
 
1167
                                   "'%s'" % self.path))
1015
1168
            if not os.access(os.path.dirname(self.path), os.W_OK):
1016
 
                raise ValueError, _("No write access to directory '%s'") % \
1017
 
                                    os.path.dirname(self.path)
 
1169
                raise ValueError(_("No write access to directory '%s'") %
 
1170
                                   os.path.dirname(self.path))
1018
1171
 
1019
1172
        # Applicable for managed or local storage
1020
1173
        ret = self.is_size_conflict()
1021
1174
        if ret[0]:
1022
 
            raise ValueError, ret[1]
 
1175
            raise ValueError(ret[1])
1023
1176
        elif ret[1]:
1024
1177
            logging.warn(ret[1])
1025
1178
 
1038
1191
            text = (_("Cloning %(srcfile)s") %
1039
1192
                    {'srcfile' : os.path.basename(self.clone_path)})
1040
1193
        else:
1041
 
            text=_("Creating storage file %s") % os.path.basename(self.path)
 
1194
            text = _("Creating storage file %s") % os.path.basename(self.path)
1042
1195
 
1043
1196
        size_bytes = long(self.size * 1024L * 1024L * 1024L)
1044
1197
        progresscb.start(filename=self.path, size=long(size_bytes),
1051
1204
 
1052
1205
                if (not _util.is_vdisk(self.clone_path) or
1053
1206
                    os.path.exists(self.path)):
1054
 
                    raise RuntimeError, _("copying to an existing vdisk is not"
1055
 
                                          " supported")
 
1207
                    raise RuntimeError(_("copying to an existing vdisk is not"
 
1208
                                         " supported"))
1056
1209
                if not _vdisk_clone(self.clone_path, self.path):
1057
 
                    raise RuntimeError, _("failed to clone disk")
 
1210
                    raise RuntimeError(_("failed to clone disk"))
1058
1211
                progresscb.end(size_bytes)
1059
1212
 
1060
1213
            else:
1065
1218
            # Create vdisk
1066
1219
            progresscb.update(1024)
1067
1220
            if not _vdisk_create(self.path, size_bytes, "vmdk", self.sparse):
1068
 
                raise RuntimeError, _("Error creating vdisk %s" % self.path)
 
1221
                raise RuntimeError(_("Error creating vdisk %s" % self.path))
1069
1222
 
1070
1223
            progresscb.end(self.size)
1071
1224
        else:
1112
1265
                if fd:
1113
1266
                    os.close(fd)
1114
1267
        else:
1115
 
            clone_block_size = 1024*1024*10
 
1268
            clone_block_size = 1024 * 1024 * 10
1116
1269
            sparse = False
1117
1270
 
1118
1271
        logging.debug("Local Cloning %s to %s, sparse=%s, block_size=%s" %
1126
1279
                src_fd = os.open(self.clone_path, os.O_RDONLY)
1127
1280
                dst_fd = os.open(self.path, os.O_WRONLY | os.O_CREAT)
1128
1281
 
1129
 
                i=0
 
1282
                i = 0
1130
1283
                while 1:
1131
1284
                    l = os.read(src_fd, clone_block_size)
1132
1285
                    s = len(l)
1188
1341
                              (self.path, storage_label, self.selinux_label))
1189
1342
                _util.selinux_setfilecon(self.path, self.selinux_label)
1190
1343
 
1191
 
    def get_xml_config(self, disknode=None):
 
1344
    def _get_xml_config(self, disknode=None):
1192
1345
        """
1193
1346
        @param disknode: device name in host (xvda, hdb, etc.). self.target
1194
1347
                         takes precedence.
1283
1436
 
1284
1437
 
1285
1438
            if msg:
1286
 
                msg += _(" %d M requested > %d M available") % \
1287
 
                        ((need / (1024*1024)), (avail / (1024*1024)))
 
1439
                msg += (_(" %d M requested > %d M available") %
 
1440
                        ((need / (1024 * 1024)), (avail / (1024 * 1024))))
1288
1441
        return (ret, msg)
1289
1442
 
1290
1443
    def is_conflict_disk(self, conn, return_names=False):
1330
1483
        Return True if we have the requisite libvirt and library support
1331
1484
        for selinux commands
1332
1485
        """
1333
 
        if (not self._caps and False):
1334
 
            #self._caps.host.secmodel is None or
1335
 
            #self._caps.host.secmodel.model != "selinux"):
 
1486
        caps = self._get_caps()
 
1487
        if (not caps and False):
 
1488
            #caps.host.secmodel is None or
 
1489
            #caps.host.secmodel.model != "selinux"):
1336
1490
            # XXX: Libvirt support isn't strictly required, but all the
1337
1491
            #      our label guesses are built with svirt in mind
1338
1492
            return False
1345
1499
            #      this will need changing.
1346
1500
            return False
1347
1501
 
1348
 
        elif self.__storage_specified() and self.path:
 
1502
        elif self.__managed_storage() and self.path:
1349
1503
            try:
1350
1504
                statinfo = os.stat(self.path)
1351
1505
            except: