~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): Guido Günther
  • Date: 2009-12-07 10:04:22 UTC
  • mto: (1.6.3 sid)
  • mto: This revision was merged to the branch mainline in revision 20.
  • Revision ID: james.westby@ubuntu.com-20091207100422-2izjffd5ljvqun47
Tags: upstream-0.500.1
Import upstream version 0.500.1

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, statvfs
 
22
import os, stat, pwd, statvfs
23
23
import subprocess
24
24
import logging
 
25
import re
 
26
 
25
27
import urlgrabber.progress as progress
26
28
import libvirt
27
29
 
55
57
    except OSError:
56
58
        return False
57
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
    proc = subprocess.Popen(cmd,
 
103
                            stdout=subprocess.PIPE,
 
104
                            stderr=subprocess.PIPE)
 
105
    out, err = proc.communicate()
 
106
 
 
107
    if proc.returncode != 0:
 
108
        logging.debug("Cmd '%s' failed: %s" % (cmd, err))
 
109
        return False
 
110
 
 
111
    return bool(re.search("user:%s:..x" % username, out))
 
112
 
 
113
 
58
114
class VirtualDisk(VirtualDevice):
59
115
    """
60
116
    Builds a libvirt domain disk xml description
142
198
 
143
199
        return False
144
200
 
 
201
    @staticmethod
 
202
    def check_path_search_for_user(conn, path, username):
 
203
        """
 
204
        Check if the passed user has search permissions for all the
 
205
        directories in the disk path.
 
206
 
 
207
        @return: List of the directories the user cannot search, or empty list
 
208
        @rtype : C{list}
 
209
        """
 
210
        if _util.is_uri_remote(conn.getURI()):
 
211
            return []
 
212
 
 
213
        try:
 
214
            uid = _name_uid(username)
 
215
        except Exception, e:
 
216
            logging.debug("Error looking up username: %s" % str(e))
 
217
            return []
 
218
 
 
219
        fixlist = []
 
220
 
 
221
        if os.path.isdir(path):
 
222
            dirname = path
 
223
            base = "-"
 
224
        else:
 
225
            dirname, base = os.path.split(path)
 
226
 
 
227
        while base:
 
228
            if not _is_dir_searchable(uid, username, dirname):
 
229
                fixlist.append(dirname)
 
230
 
 
231
            dirname, base = os.path.split(dirname)
 
232
 
 
233
        return fixlist
 
234
 
 
235
    @staticmethod
 
236
    def fix_path_search_for_user(conn, path, username):
 
237
        """
 
238
        Try to fix any permission problems found by check_path_search_for_user
 
239
 
 
240
        @return: Return a dictionary of entries { broken path : error msg }
 
241
        @rtype : C{dict}
 
242
        """
 
243
        def fix_perms(dirname, useacl=True):
 
244
            if useacl:
 
245
                cmd = ["setfacl", "--modify", "user:%s:x" % username, dirname]
 
246
                proc = subprocess.Popen(cmd,
 
247
                                        stdout=subprocess.PIPE,
 
248
                                        stderr=subprocess.PIPE)
 
249
                out, err = proc.communicate()
 
250
 
 
251
                logging.debug("Ran command '%s'" % cmd)
 
252
                if out or err:
 
253
                    logging.debug("out=%s\nerr=%s" % (out, err))
 
254
 
 
255
                if proc.returncode != 0:
 
256
                    raise ValueError(err)
 
257
            else:
 
258
                mode = os.stat(dirname).st_mode
 
259
                os.chmod(dirname, mode | stat.S_IXOTH)
 
260
 
 
261
        fixlist = VirtualDisk.check_path_search_for_user(conn, path, username)
 
262
        if not fixlist:
 
263
            return []
 
264
 
 
265
        fixlist.reverse()
 
266
        errdict = {}
 
267
 
 
268
        useacl = True
 
269
        for dirname in fixlist:
 
270
            try:
 
271
                try:
 
272
                    fix_perms(dirname, useacl)
 
273
                except:
 
274
                    # If acl fails, fall back to chmod and retry
 
275
                    if not useacl:
 
276
                        raise
 
277
                    useacl = False
 
278
 
 
279
                    fix_perms(dirname, useacl)
 
280
            except Exception, e:
 
281
                errdict[dirname] =  str(e)
 
282
 
 
283
        return errdict
 
284
 
 
285
 
145
286
    def __init__(self, path=None, size=None, transient=False, type=None,
146
287
                 device=DEVICE_DISK, driverName=None, driverType=None,
147
288
                 readOnly=False, sparse=True, conn=None, volObject=None,
148
289
                 volInstall=None, volName=None, bus=None, shareable=False,
149
 
                 driverCache=None, selinuxLabel=None):
 
290
                 driverCache=None, selinuxLabel=None, format=None):
150
291
        """
151
292
        @param path: filesystem path to the disk image.
152
293
        @type path: C{str}
183
324
        @type driverCache: member of cache_types
184
325
        @param selinuxLabel: Used for labelling new or relabel existing storage
185
326
        @type selinuxLabel: C{str}
 
327
        @param format: Storage volume format to use when creating storage
 
328
        @type format: C{str}
186
329
        """
187
330
 
188
331
        VirtualDevice.__init__(self, conn=conn)
200
343
        self._driver_cache = None
201
344
        self._selinux_label = None
202
345
        self._clone_path = None
 
346
        self._format = None
203
347
 
204
348
        # XXX: No property methods for these
205
349
        self.transient = transient
219
363
        self._set_shareable(shareable, validate=False)
220
364
        self._set_driver_cache(driverCache, validate=False)
221
365
        self._set_selinux_label(selinuxLabel, validate=False)
 
366
        self._set_format(format, validate=False)
222
367
 
223
368
        if volName:
224
369
            self.__lookup_vol_name(volName)
375
520
        self.__validate_wrapper("_selinux_label", val, validate)
376
521
    selinux_label = property(_get_selinux_label, _set_selinux_label)
377
522
 
 
523
    def _get_format(self):
 
524
        return self._format
 
525
    def _set_format(self, val, validate=True):
 
526
        if val is not None:
 
527
            self._check_str(val, "format")
 
528
        self.__validate_wrapper("_format", val, validate)
 
529
    format = property(_get_format, _set_format)
 
530
 
378
531
    # Validation assistance methods
379
532
 
380
533
    # Initializes attribute if it hasn't been done, then validates args.
392
545
                setattr(self, varname, orig)
393
546
                raise
394
547
 
 
548
    def __set_format(self):
 
549
        if not self.format:
 
550
            return
 
551
 
 
552
        if not self.__creating_storage():
 
553
            return
 
554
 
 
555
        if self.vol_install:
 
556
            if not hasattr(self.vol_install, "format"):
 
557
                raise ValueError(_("Storage type does not support format "
 
558
                                   "parameter."))
 
559
            if self.vol_install.format != self.format:
 
560
                self.vol_install.format = self.format
 
561
 
 
562
        elif self.format != "raw":
 
563
            raise RuntimeError(_("Format cannot be specified for "
 
564
                                 "unmanaged storage."))
 
565
 
395
566
    def __set_size(self):
396
567
        """
397
568
        Fill in 'size' attribute for existing storage.
460
631
 
461
632
        http://lists.gnu.org/archive/html/qemu-devel/2008-04/msg00675.html
462
633
        """
463
 
        drvname = None
464
 
        drvtype = None
 
634
        drvname = self._driverName
 
635
        drvtype = self._driverType
465
636
 
466
637
        if self.conn:
467
638
            driver = _util.get_uri_driver(self._get_uri())
469
640
                drvname = self.DRIVER_QEMU
470
641
 
471
642
        if self.vol_object:
472
 
            drvtype = _util.get_xml_path(self.vol_object.XMLDesc(0),
473
 
                                         "/volume/target/format")
 
643
            fmt = _util.get_xml_path(self.vol_object.XMLDesc(0),
 
644
                                     "/volume/target/format/@type")
 
645
            if drvname == self.DRIVER_QEMU:
 
646
                drvtype = _qemu_sanitize_drvtype(self.type, fmt)
474
647
 
475
648
        elif self.vol_install:
476
649
            if drvname == self.DRIVER_QEMU:
477
 
                if self.vol_install.file_type == libvirt.VIR_STORAGE_VOL_FILE:
478
 
                    drvtype = self.vol_install.format
479
 
                else:
480
 
                    drvtype = self.DRIVER_QEMU_RAW
 
650
                if hasattr(self.vol_install, "format"):
 
651
                    drvtype = _qemu_sanitize_drvtype(self.type,
 
652
                                                     self.vol_install.format)
481
653
 
482
654
        elif self.__creating_storage():
483
655
            if drvname == self.DRIVER_QEMU:
492
664
            # User already set driverName to a different value, respect that
493
665
            return
494
666
 
495
 
        self._driverName = drvname
496
 
        self._driverType = drvtype
 
667
        self._driverName = drvname or None
 
668
        self._driverType = drvtype or None
497
669
 
498
670
    def __lookup_vol_name(self, name_tuple):
499
671
        """
699
871
        managed_storage = self.__storage_specified()
700
872
        create_media = self.__creating_storage()
701
873
 
 
874
        self.__set_dev_type()
702
875
        self.__set_size()
 
876
        self.__set_format()
 
877
        self.__set_driver()
703
878
 
704
879
        if not self.selinux_label:
705
880
            # If we are using existing storage, pull the label from it
714
889
 
715
890
            self._selinux_label = context or ""
716
891
 
717
 
        # Set driverName + driverType
718
 
        self.__set_driver()
719
 
 
720
892
        # If not creating the storage, our job is easy
721
893
        if not create_media:
722
894
            # Make sure we have access to the local path
726
898
                    raise ValueError(_("The path '%s' must be a file or a "
727
899
                                       "device, not a directory") % self.path)
728
900
 
729
 
            self.__set_dev_type()
730
901
            return True
731
902
 
732
903
 
739
910
            if self.type is self.TYPE_BLOCK:
740
911
                raise ValueError, _("Local block device path '%s' must "
741
912
                                    "exist.") % self.path
742
 
            self.set_type(self.TYPE_FILE, validate=False)
743
913
 
744
914
            # Path doesn't exist: make sure we have write access to dir
745
915
            if not os.access(os.path.dirname(self.path), os.R_OK):
751
921
            if not os.access(os.path.dirname(self.path), os.W_OK):
752
922
                raise ValueError, _("No write access to directory '%s'") % \
753
923
                                    os.path.dirname(self.path)
754
 
        else:
755
 
            # Set dev type from existing storage
756
 
            self.__set_dev_type()
757
924
 
758
925
        # Applicable for managed or local storage
759
926
        ret = self.is_size_conflict()
859
1026
 
860
1027
        zeros = '\0' * 4096
861
1028
 
 
1029
        src_fd, dst_fd = None, None
862
1030
        try:
863
 
            src_fd = os.open(self.clone_path, os.O_RDONLY)
864
 
            dst_fd = os.open(self.path, os.O_WRONLY | os.O_CREAT)
 
1031
            try:
 
1032
                src_fd = os.open(self.clone_path, os.O_RDONLY)
 
1033
                dst_fd = os.open(self.path, os.O_WRONLY | os.O_CREAT)
865
1034
 
866
 
            i=0
867
 
            while 1:
868
 
                l = os.read(src_fd, clone_block_size)
869
 
                s = len(l)
870
 
                if s == 0:
871
 
                    meter.end(size_bytes)
872
 
                    break
873
 
                # check sequence of zeros
874
 
                if sparse and zeros == l:
875
 
                    os.lseek(dst_fd, s, 1)
876
 
                else:
877
 
                    b = os.write(dst_fd, l)
878
 
                    if s != b:
879
 
                        meter.end(i)
 
1035
                i=0
 
1036
                while 1:
 
1037
                    l = os.read(src_fd, clone_block_size)
 
1038
                    s = len(l)
 
1039
                    if s == 0:
 
1040
                        meter.end(size_bytes)
880
1041
                        break
881
 
                i += s
882
 
                if i < size_bytes:
883
 
                    meter.update(i)
884
 
 
 
1042
                    # check sequence of zeros
 
1043
                    if sparse and zeros == l:
 
1044
                        os.lseek(dst_fd, s, 1)
 
1045
                    else:
 
1046
                        b = os.write(dst_fd, l)
 
1047
                        if s != b:
 
1048
                            meter.end(i)
 
1049
                            break
 
1050
                    i += s
 
1051
                    if i < size_bytes:
 
1052
                        meter.update(i)
 
1053
            except OSError, e:
 
1054
                raise RuntimeError(_("Error cloning diskimage %s to %s: %s") %
 
1055
                                       (self.clone_path, self.path, str(e)))
885
1056
        finally:
886
1057
            if src_fd is not None:
887
1058
                os.close(src_fd)
1142
1313
        the passed parameters.
1143
1314
        @returns: str prefix, or None if no reasonable guess can be made
1144
1315
        """
 
1316
        # The upper limits here aren't necessarilly 1024, but let the HV
 
1317
        # error as appropriate.
1145
1318
        if self.bus == "virtio":
1146
 
            return ("vd", 16)
 
1319
            return ("vd", 1024)
1147
1320
        elif self.bus == "scsi" or self.bus == "usb":
1148
 
            return ("sd", 16)
 
1321
            return ("sd", 1024)
1149
1322
        elif self.bus == "xen":
1150
 
            return ("xvd", 16)
 
1323
            return ("xvd", 1024)
1151
1324
        elif self.bus == "fdc" or self.device == self.DEVICE_FLOPPY:
1152
1325
            return ("fd", 2)
1153
1326
        elif self.bus == "ide":