/[smeserver]/cdrom.image/sme9/updates/storage/formats/fs.py
ViewVC logotype

Annotation of /cdrom.image/sme9/updates/storage/formats/fs.py

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (hide annotations) (download) (as text)
Sun Oct 20 23:12:54 2013 UTC (11 years ago) by charliebrady
Branch: MAIN
Content type: text/x-python
Add devicelibs and formats subdirectories of storage, and contained .py
files, in preparation to modifying to allow degraded RAID install.

1 charliebrady 1.1 # filesystems.py
2     # Filesystem classes for anaconda's storage configuration module.
3     #
4     # Copyright (C) 2009 Red Hat, Inc.
5     #
6     # This copyrighted material is made available to anyone wishing to use,
7     # modify, copy, or redistribute it subject to the terms and conditions of
8     # the GNU General Public License v.2, or (at your option) any later version.
9     # This program is distributed in the hope that it will be useful, but WITHOUT
10     # ANY WARRANTY expressed or implied, including the implied warranties of
11     # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
12     # Public License for more details. You should have received a copy of the
13     # GNU General Public License along with this program; if not, write to the
14     # Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15     # 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
16     # source code or documentation are not subject to the GNU General Public
17     # License and may only be used or replicated with the express permission of
18     # Red Hat, Inc.
19     #
20     # Red Hat Author(s): Dave Lehman <dlehman@redhat.com>
21     # David Cantrell <dcantrell@redhat.com>
22     #
23    
24     """ Filesystem classes for use by anaconda.
25    
26     TODO:
27     - migration
28     - bug 472127: allow creation of tmpfs filesystems (/tmp, /var/tmp, &c)
29     """
30     import math
31     import os
32     import sys
33     import tempfile
34     import selinux
35     import isys
36    
37     from ..errors import *
38     from . import DeviceFormat, register_device_format
39     import iutil
40     from flags import flags
41     from parted import fileSystemType
42     from ..storage_log import log_method_call
43    
44     import logging
45     log = logging.getLogger("storage")
46    
47     import gettext
48     _ = lambda x: gettext.ldgettext("anaconda", x)
49    
50     try:
51     lost_and_found_context = selinux.matchpathcon("/lost+found", 0)[1]
52     except OSError:
53     lost_and_found_context = None
54    
55     fs_configs = {}
56    
57     def get_kernel_filesystems():
58     fs_list = []
59     for line in open("/proc/filesystems").readlines():
60     fs_list.append(line.split()[-1])
61     return fs_list
62    
63     global kernel_filesystems
64     kernel_filesystems = get_kernel_filesystems()
65    
66     def fsConfigFromFile(config_file):
67     """ Generate a set of attribute name/value pairs with which a
68     filesystem type can be defined.
69    
70     The following config file would define a filesystem identical to
71     the static Ext3FS class definition:
72    
73     type = ext3
74     mkfs = "mke2fs"
75     resizefs = "resize2fs"
76     labelfs = "e2label"
77     fsck = "e2fsck"
78     packages = ["e2fsprogs"]
79     formattable = True
80     supported = True
81     resizable = True
82     bootable = True
83     linuxNative = True
84     maxSize = 8 * 1024 * 1024
85     minSize = 0
86     defaultFormatOptions = "-t ext3"
87     defaultMountOptions = "defaults"
88    
89     """
90     # XXX NOTUSED
91     lines = open(config_file).readlines()
92     fs_attrs = {}
93     for line in lines:
94     (key, value) = [t.strip() for t in line.split("=")]
95     if not hasattr(FS, "_" + key):
96     print "invalid key: %s" % key
97     continue
98    
99     fs_attrs[key] = value
100    
101     if not fs_attrs.has_key("type"):
102     raise ValueError, _("filesystem configuration missing a type")
103    
104     # XXX what's the policy about multiple configs for a given type?
105     fs_configs[fs_attrs['type']] = fs_attrs
106    
107     class FS(DeviceFormat):
108     """ Filesystem class. """
109     _type = "Abstract Filesystem Class" # fs type name
110     _mountType = None # like _type but for passing to mount
111     _name = None
112     _mkfs = "" # mkfs utility
113     _modules = [] # kernel modules required for support
114     _resizefs = "" # resize utility
115     _labelfs = "" # labeling utility
116     _fsck = "" # fs check utility
117     _fsckErrors = {} # fs check command error codes & msgs
118     _migratefs = "" # fs migration utility
119     _infofs = "" # fs info utility
120     _defaultFormatOptions = [] # default options passed to mkfs
121     _defaultMountOptions = ["defaults"] # default options passed to mount
122     _defaultLabelOptions = []
123     _defaultCheckOptions = []
124     _defaultMigrateOptions = []
125     _defaultInfoOptions = []
126     _migrationTarget = None
127     _existingSizeFields = []
128     _fsProfileSpecifier = None # mkfs option specifying fsprofile
129    
130     def __init__(self, *args, **kwargs):
131     """ Create a FS instance.
132    
133     Keyword Args:
134    
135     device -- path to the device containing the filesystem
136     mountpoint -- the filesystem's mountpoint
137     label -- the filesystem label
138     uuid -- the filesystem UUID
139     mountopts -- mount options for the filesystem
140     size -- the filesystem's size in MiB
141     exists -- indicates whether this is an existing filesystem
142    
143     """
144     if self.__class__ is FS:
145     raise TypeError("FS is an abstract class.")
146    
147     DeviceFormat.__init__(self, *args, **kwargs)
148     self.mountpoint = kwargs.get("mountpoint")
149     self.mountopts = kwargs.get("mountopts")
150     self.label = kwargs.get("label")
151     self.fsprofile = kwargs.get("fsprofile")
152    
153     # filesystem size does not necessarily equal device size
154     self._size = kwargs.get("size", 0)
155     self._minInstanceSize = None # min size of this FS instance
156     self._mountpoint = None # the current mountpoint when mounted
157     if self.exists and self.supported:
158     self._size = self._getExistingSize()
159     foo = self.minSize # force calculation of minimum size
160    
161     self._targetSize = self._size
162    
163     if self.supported:
164     self.loadModule()
165    
166     def __str__(self):
167     s = DeviceFormat.__str__(self)
168     s += (" mountpoint = %(mountpoint)s mountopts = %(mountopts)s\n"
169     " label = %(label)s size = %(size)s"
170     " targetSize = %(targetSize)s\n" %
171     {"mountpoint": self.mountpoint, "mountopts": self.mountopts,
172     "label": self.label, "size": self._size,
173     "targetSize": self.targetSize})
174     return s
175    
176     @property
177     def dict(self):
178     d = super(FS, self).dict
179     d.update({"mountpoint": self.mountpoint, "size": self._size,
180     "label": self.label, "targetSize": self.targetSize,
181     "mountable": self.mountable,
182     "migratable": self.migratable})
183     return d
184    
185     def _setTargetSize(self, newsize):
186     """ Set a target size for this filesystem. """
187     if not self.exists:
188     raise FSError("filesystem has not been created")
189    
190     if newsize is None:
191     # unset any outstanding resize request
192     self._targetSize = self._size
193     return
194    
195     if not self.minSize <= newsize < self.maxSize:
196     raise ValueError("invalid target size request")
197    
198     self._targetSize = newsize
199    
200     def _getTargetSize(self):
201     """ Get this filesystem's target size. """
202     return self._targetSize
203    
204     targetSize = property(_getTargetSize, _setTargetSize,
205     doc="Target size for this filesystem")
206    
207     def _getSize(self):
208     """ Get this filesystem's size. """
209     size = self._size
210     if self.resizable and self.targetSize != size:
211     size = self.targetSize
212     return size
213    
214     size = property(_getSize, doc="This filesystem's size, accounting "
215     "for pending changes")
216    
217     def _getExistingSize(self):
218     """ Determine the size of this filesystem. Filesystem must
219     exist. Each filesystem varies, but the general procedure
220     is to run the filesystem dump or info utility and read
221     the block size and number of blocks for the filesystem
222     and compute megabytes from that.
223    
224     The loop that reads the output from the infofsProg is meant
225     to be simple, but take in to account variations in output.
226     The general procedure:
227     1) Capture output from infofsProg.
228     2) Iterate over each line of the output:
229     a) Trim leading and trailing whitespace.
230     b) Break line into fields split on ' '
231     c) If line begins with any of the strings in
232     _existingSizeFields, start at the end of
233     fields and take the first one that converts
234     to a long. Store this in the values list.
235     d) Repeat until the values list length equals
236     the _existingSizeFields length.
237     3) If the length of the values list equals the length
238     of _existingSizeFields, compute the size of this
239     filesystem by multiplying all of the values together
240     to get bytes, then convert to megabytes. Return
241     this value.
242     4) If we were unable to capture all fields, return 0.
243    
244     The caller should catch exceptions from this method. Any
245     exception raised indicates a need to change the fields we
246     are looking for, the command to run and arguments, or
247     something else. If you catch an exception from this method,
248     assume the filesystem cannot be resized.
249     """
250     size = self._size
251    
252     if self.infofsProg and self.mountable and self.exists and not size:
253     try:
254     values = []
255     argv = self._defaultInfoOptions + [ self.device ]
256    
257     buf = iutil.execWithCapture(self.infofsProg, argv,
258     stderr="/dev/tty5")
259    
260     for line in buf.splitlines():
261     found = False
262    
263     line = line.strip()
264     tmp = line.split(' ')
265     tmp.reverse()
266    
267     for field in self._existingSizeFields:
268     if line.startswith(field):
269     for subfield in tmp:
270     try:
271     values.append(long(subfield))
272     found = True
273     break
274     except ValueError:
275     continue
276    
277     if found:
278     break
279    
280     if len(values) == len(self._existingSizeFields):
281     break
282    
283     if len(values) != len(self._existingSizeFields):
284     return 0
285    
286     size = 1
287     for value in values:
288     size *= value
289    
290     # report current size as megabytes
291     size = math.floor(size / 1024.0 / 1024.0)
292     except Exception as e:
293     log.error("failed to obtain size of filesystem on %s: %s"
294     % (self.device, e))
295    
296     return size
297    
298     @property
299     def currentSize(self):
300     """ The filesystem's current actual size. """
301     size = 0
302     if self.exists:
303     size = self._size
304     return float(size)
305    
306     def _getFormatOptions(self, options=None):
307     argv = []
308     if options and isinstance(options, list):
309     argv.extend(options)
310     argv.extend(self.defaultFormatOptions)
311     if self._fsProfileSpecifier and self.fsprofile:
312     argv.extend([self._fsProfileSpecifier, self.fsprofile])
313     argv.append(self.device)
314     return argv
315    
316     def doFormat(self, *args, **kwargs):
317     """ Create the filesystem.
318    
319     Arguments:
320    
321     None
322    
323     Keyword Arguments:
324    
325     intf -- InstallInterface instance
326     options -- list of options to pass to mkfs
327    
328     """
329     log_method_call(self, type=self.mountType, device=self.device,
330     mountpoint=self.mountpoint)
331    
332     intf = kwargs.get("intf")
333     options = kwargs.get("options")
334    
335     if self.exists:
336     raise FormatCreateError("filesystem already exists", self.device)
337    
338     if not self.formattable:
339     return
340    
341     if not self.mkfsProg:
342     return
343    
344     if self.exists:
345     return
346    
347     if not os.path.exists(self.device):
348     raise FormatCreateError("device does not exist", self.device)
349    
350     argv = self._getFormatOptions(options=options)
351    
352     w = None
353     if intf:
354     w = intf.progressWindow(_("Formatting"),
355     _("Creating %s filesystem on %s")
356     % (self.type, self.device),
357     100, pulse = True)
358    
359     try:
360     rc = iutil.execWithPulseProgress(self.mkfsProg,
361     argv,
362     stdout="/dev/tty5",
363     stderr="/dev/tty5",
364     progress=w)
365     except Exception as e:
366     raise FormatCreateError(e, self.device)
367     finally:
368     if w:
369     w.pop()
370    
371     if rc:
372     raise FormatCreateError("format failed: %s" % rc, self.device)
373    
374     self.exists = True
375     self.notifyKernel()
376    
377     if self.label:
378     self.writeLabel(self.label)
379    
380     def doMigrate(self, intf=None):
381     if not self.exists:
382     raise FSError("filesystem has not been created")
383    
384     if not self.migratable or not self.migrate:
385     return
386    
387     if not os.path.exists(self.device):
388     raise FSError("device does not exist")
389    
390     # if journal already exists skip
391     if isys.ext2HasJournal(self.device):
392     log.info("Skipping migration of %s, has a journal already."
393     % self.device)
394     return
395    
396     argv = self._defaultMigrateOptions[:]
397     argv.append(self.device)
398     try:
399     rc = iutil.execWithRedirect(self.migratefsProg,
400     argv,
401     stdout = "/dev/tty5",
402     stderr = "/dev/tty5")
403     except Exception as e:
404     raise FSMigrateError("filesystem migration failed: %s" % e,
405     self.device)
406    
407     if rc:
408     raise FSMigrateError("filesystem migration failed: %s" % rc,
409     self.device)
410    
411     # the other option is to actually replace this instance with an
412     # instance of the new filesystem type.
413     self._type = self.migrationTarget
414     self._mountType = self.migrationTarget
415    
416     @property
417     def resizeArgs(self):
418     argv = [self.device, "%d" % (self.targetSize,)]
419     return argv
420    
421     def doResize(self, *args, **kwargs):
422     """ Resize this filesystem to new size @newsize.
423    
424     Arguments:
425    
426     None
427    
428     Keyword Arguments:
429    
430     intf -- InstallInterface instance
431    
432     """
433     intf = kwargs.get("intf")
434    
435     if not self.exists:
436     raise FSResizeError("filesystem does not exist", self.device)
437    
438     if not self.resizable:
439     raise FSResizeError("filesystem not resizable", self.device)
440    
441     if self.targetSize == self.currentSize:
442     return
443    
444     if not self.resizefsProg:
445     return
446    
447     if not os.path.exists(self.device):
448     raise FSResizeError("device does not exist", self.device)
449    
450     self.doCheck(intf=intf)
451    
452     w = None
453     if intf:
454     w = intf.progressWindow(_("Resizing"),
455     _("Resizing filesystem on %s")
456     % (self.device,),
457     100, pulse = True)
458    
459     try:
460     rc = iutil.execWithPulseProgress(self.resizefsProg,
461     self.resizeArgs,
462     stdout="/dev/tty5",
463     stderr="/dev/tty5",
464     progress=w)
465     except Exception as e:
466     raise FSResizeError(e, self.device)
467     finally:
468     if w:
469     w.pop()
470    
471     if rc:
472     raise FSResizeError("resize failed: %s" % rc, self.device)
473    
474     self.doCheck(intf=intf)
475    
476     # XXX must be a smarter way to do this
477     self._size = self.targetSize
478     self.notifyKernel()
479    
480     def _getCheckArgs(self):
481     argv = []
482     argv.extend(self.defaultCheckOptions)
483     argv.append(self.device)
484     return argv
485    
486     def _fsckFailed(self, rc):
487     return False
488    
489     def _fsckErrorMessage(self, rc):
490     return _("Unknown return code: %d.") % (rc,)
491    
492     def doCheck(self, intf=None):
493     if not self.exists:
494     raise FSError("filesystem has not been created")
495    
496     if not self.fsckProg:
497     return
498    
499     if not os.path.exists(self.device):
500     raise FSError("device does not exist")
501    
502     w = None
503     if intf:
504     w = intf.progressWindow(_("Checking"),
505     _("Checking filesystem on %s")
506     % (self.device),
507     100, pulse = True)
508    
509     try:
510     rc = iutil.execWithPulseProgress(self.fsckProg,
511     self._getCheckArgs(),
512     stdout="/dev/tty5",
513     stderr="/dev/tty5",
514     progress = w)
515     except Exception as e:
516     raise FSError("filesystem check failed: %s" % e)
517     finally:
518     if w:
519     w.pop()
520    
521     if self._fsckFailed(rc):
522     hdr = _("%(type)s filesystem check failure on %(device)s: ") % \
523     {"type": self.type, "device": self.device}
524    
525     msg = self._fsckErrorMessage(rc)
526    
527     if intf:
528     help = _("Errors like this usually mean there is a problem "
529     "with the filesystem that will require user "
530     "interaction to repair. Before restarting "
531     "installation, reboot to rescue mode or another "
532     "system that allows you to repair the filesystem "
533     "interactively. Restart installation after you "
534     "have corrected the problems on the filesystem.")
535    
536     intf.messageWindow(_("Unrecoverable Error"),
537     hdr + "\n\n" + msg + "\n\n" + help,
538     custom_icon='error')
539     sys.exit(0)
540     else:
541     raise FSError(hdr + msg)
542    
543     def loadModule(self):
544     """Load whatever kernel module is required to support this filesystem."""
545     global kernel_filesystems
546    
547     if not self._modules or self.mountType in kernel_filesystems:
548     return
549    
550     for module in self._modules:
551     try:
552     rc = iutil.execWithRedirect("modprobe", [module],
553     stdout="/dev/tty5", stderr="/dev/tty5")
554     except Exception as e:
555     log.error("Could not load kernel module %s: %s" % (module, e))
556     self._supported = False
557     return
558    
559     if rc:
560     log.error("Could not load kernel module %s" % module)
561     self._supported = False
562     return
563    
564     # If we successfully loaded a kernel module, for this filesystem, we
565     # also need to update the list of supported filesystems.
566     kernel_filesystems = get_kernel_filesystems()
567    
568     def mount(self, *args, **kwargs):
569     """ Mount this filesystem.
570    
571     Arguments:
572    
573     None
574    
575     Keyword Arguments:
576    
577     options -- mount options (overrides all other option strings)
578     chroot -- prefix to apply to mountpoint
579     mountpoint -- mountpoint (overrides self.mountpoint)
580     """
581     options = kwargs.get("options", "")
582     chroot = kwargs.get("chroot", "/")
583     mountpoint = kwargs.get("mountpoint")
584    
585     if not self.exists:
586     raise FSError("filesystem has not been created")
587    
588     if not mountpoint:
589     mountpoint = self.mountpoint
590    
591     if not mountpoint:
592     raise FSError("no mountpoint given")
593    
594     if self.status:
595     return
596    
597     if not isinstance(self, NoDevFS) and not os.path.exists(self.device):
598     raise FSError("device %s does not exist" % self.device)
599    
600     # XXX os.path.join is FUBAR:
601     #
602     # os.path.join("/mnt/foo", "/") -> "/"
603     #
604     #mountpoint = os.path.join(chroot, mountpoint)
605     chrootedMountpoint = os.path.normpath("%s/%s" % (chroot, mountpoint))
606     iutil.mkdirChain(chrootedMountpoint)
607     if flags.selinux:
608     ret = isys.resetFileContext(mountpoint, chroot)
609     log.info("set SELinux context for mountpoint %s to %s" \
610     % (mountpoint, ret))
611    
612     # passed in options override default options
613     if not options or not isinstance(options, str):
614     options = self.options
615    
616     try:
617     rc = isys.mount(self.device, chrootedMountpoint,
618     fstype=self.mountType,
619     options=options,
620     bindMount=isinstance(self, BindFS))
621     except Exception as e:
622     raise FSError("mount failed: %s" % e)
623    
624     if rc:
625     raise FSError("mount failed: %s" % rc)
626    
627     if flags.selinux:
628     ret = isys.resetFileContext(mountpoint, chroot)
629     log.info("set SELinux context for newly mounted filesystem "
630     "root at %s to %s" %(mountpoint, ret))
631     isys.setFileContext("%s/lost+found" % mountpoint,
632     lost_and_found_context, chroot)
633    
634     self._mountpoint = chrootedMountpoint
635    
636     def remount(self, *args, **kwargs):
637     """ Remount the filesystem with new options """
638     options = kwargs.get("options", "")
639     log.info("remounting %s on %s", self.device, self._mountpoint)
640    
641     if not self.exists:
642     raise FSError("filesystem has not been created")
643    
644     if not self._mountpoint:
645     # not mounted
646     return
647    
648     if not os.path.exists(self._mountpoint):
649     raise FSError("mountpoint does not exist")
650    
651     # passed in options override default options
652     if not options or not isinstance(options, str):
653     options = self.options
654    
655     try:
656     rc = isys.mount(self.device, self._mountpoint,
657     fstype=self.mountType,
658     options=options, remount=True,
659     bindMount=isinstance(self, BindFS))
660     except Exception as e:
661     raise FSError("mount failed: %s" % e)
662    
663     if rc:
664     raise FSError("mount failed: %s" % rc)
665    
666     if flags.selinux:
667     ret = isys.resetFileContext(self._mountpoint, "")
668     log.info("set SELinux context for newly mounted filesystem "
669     "root at %s to %s" %(self._mountpoint, ret))
670     isys.setFileContext("%s/lost+found" % self._mountpoint,
671     lost_and_found_context, "")
672    
673    
674     def unmount(self):
675     """ Unmount this filesystem. """
676     if not self.exists:
677     raise FSError("filesystem has not been created")
678    
679     if not self._mountpoint:
680     # not mounted
681     return
682    
683     if not os.path.exists(self._mountpoint):
684     raise FSError("mountpoint does not exist")
685    
686     rc = isys.umount(self._mountpoint, removeDir = False)
687     if rc:
688     raise FSError("umount failed")
689    
690     self._mountpoint = None
691    
692     def _getLabelArgs(self, label):
693     argv = []
694     argv.extend(self.defaultLabelOptions)
695     argv.extend([self.device, label])
696     return argv
697    
698     def writeLabel(self, label):
699     """ Create a label for this filesystem. """
700     if not self.exists:
701     raise FSError("filesystem has not been created")
702    
703     if not self.labelfsProg:
704     return
705    
706     if not os.path.exists(self.device):
707     raise FSError("device does not exist")
708    
709     argv = self._getLabelArgs(label)
710     rc = iutil.execWithRedirect(self.labelfsProg,
711     argv,
712     stderr="/dev/tty5")
713     if rc:
714     raise FSError("label failed")
715    
716     self.label = label
717     self.notifyKernel()
718    
719     @property
720     def isDirty(self):
721     return False
722    
723     @property
724     def mkfsProg(self):
725     """ Program used to create filesystems of this type. """
726     return self._mkfs
727    
728     @property
729     def fsckProg(self):
730     """ Program used to check filesystems of this type. """
731     return self._fsck
732    
733     @property
734     def resizefsProg(self):
735     """ Program used to resize filesystems of this type. """
736     return self._resizefs
737    
738     @property
739     def labelfsProg(self):
740     """ Program used to manage labels for this filesystem type. """
741     return self._labelfs
742    
743     @property
744     def migratefsProg(self):
745     """ Program used to migrate filesystems of this type. """
746     return self._migratefs
747    
748     @property
749     def infofsProg(self):
750     """ Program used to get information for this filesystem type. """
751     return self._infofs
752    
753     @property
754     def migrationTarget(self):
755     return self._migrationTarget
756    
757     @property
758     def utilsAvailable(self):
759     # we aren't checking for fsck because we shouldn't need it
760     for prog in [self.mkfsProg, self.resizefsProg, self.labelfsProg,
761     self.infofsProg]:
762     if not prog:
763     continue
764    
765     if not filter(lambda d: os.access("%s/%s" % (d, prog), os.X_OK),
766     os.environ["PATH"].split(":")):
767     return False
768    
769     return True
770    
771     @property
772     def supported(self):
773     log_method_call(self, supported=self._supported)
774     return self._supported and self.utilsAvailable
775    
776     @property
777     def mountable(self):
778     canmount = (self.mountType in kernel_filesystems) or \
779     (os.access("/sbin/mount.%s" % (self.mountType,), os.X_OK))
780     modpath = os.path.realpath(os.path.join("/lib/modules", os.uname()[2]))
781     modname = "%s.ko" % self.mountType
782    
783     if not canmount and os.path.isdir(modpath):
784     for root, dirs, files in os.walk(modpath):
785     have = filter(lambda x: x.startswith(modname), files)
786     if len(have) == 1 and have[0].startswith(modname):
787     return True
788    
789     return canmount
790    
791     @property
792     def resizable(self):
793     """ Can formats of this filesystem type be resized? """
794     return super(FS, self).resizable and self.utilsAvailable
795    
796     @property
797     def defaultFormatOptions(self):
798     """ Default options passed to mkfs for this filesystem type. """
799     # return a copy to prevent modification
800     return self._defaultFormatOptions[:]
801    
802     @property
803     def defaultMountOptions(self):
804     """ Default options passed to mount for this filesystem type. """
805     # return a copy to prevent modification
806     return self._defaultMountOptions[:]
807    
808     @property
809     def defaultLabelOptions(self):
810     """ Default options passed to labeler for this filesystem type. """
811     # return a copy to prevent modification
812     return self._defaultLabelOptions[:]
813    
814     @property
815     def defaultCheckOptions(self):
816     """ Default options passed to checker for this filesystem type. """
817     # return a copy to prevent modification
818     return self._defaultCheckOptions[:]
819    
820     def _getOptions(self):
821     options = ",".join(self.defaultMountOptions)
822     if self.mountopts:
823     # XXX should we clobber or append?
824     options = self.mountopts
825     return options
826    
827     def _setOptions(self, options):
828     self.mountopts = options
829    
830     options = property(_getOptions, _setOptions)
831    
832     def _isMigratable(self):
833     """ Can filesystems of this type be migrated? """
834     return bool(self._migratable and self.migratefsProg and
835     filter(lambda d: os.access("%s/%s"
836     % (d, self.migratefsProg,),
837     os.X_OK),
838     os.environ["PATH"].split(":")) and
839     self.migrationTarget)
840    
841     migratable = property(_isMigratable)
842    
843     def _setMigrate(self, migrate):
844     if not migrate:
845     self._migrate = migrate
846     return
847    
848     if self.migratable and self.exists:
849     self._migrate = migrate
850     else:
851     raise ValueError("cannot set migrate on non-migratable filesystem")
852    
853     migrate = property(lambda f: f._migrate, lambda f,m: f._setMigrate(m))
854    
855     @property
856     def type(self):
857     _type = self._type
858     if self.migrate:
859     _type = self.migrationTarget
860    
861     return _type
862    
863     @property
864     def mountType(self):
865     if not self._mountType:
866     self._mountType = self._type
867    
868     return self._mountType
869    
870     # These methods just wrap filesystem-specific methods in more
871     # generically named methods so filesystems and formatted devices
872     # like swap and LVM physical volumes can have a common API.
873     def create(self, *args, **kwargs):
874     if self.exists:
875     raise FSError("filesystem already exists")
876    
877     DeviceFormat.create(self, *args, **kwargs)
878    
879     return self.doFormat(*args, **kwargs)
880    
881     def setup(self, *args, **kwargs):
882     """ Mount the filesystem.
883    
884     The filesystem will be mounted at the directory indicated by
885     self.mountpoint.
886     """
887     return self.mount(**kwargs)
888    
889     def teardown(self, *args, **kwargs):
890     return self.unmount(*args, **kwargs)
891    
892     @property
893     def status(self):
894     # FIXME check /proc/mounts or similar
895     if not self.exists:
896     return False
897     return self._mountpoint is not None
898    
899     def writeKS(self, f):
900     f.write("%s --fstype=%s" % (self.mountpoint, self.type))
901    
902     if self.label:
903     f.write(" --label=\"%s\"" % self.label)
904    
905     if self.fsprofile:
906     f.write(" --fsprofile=\"%s\"" % self.fsprofile)
907    
908    
909     class Ext2FS(FS):
910     """ ext2 filesystem. """
911     _type = "ext2"
912     _mkfs = "mke2fs"
913     _modules = ["ext2"]
914     _resizefs = "resize2fs"
915     _labelfs = "e2label"
916     _fsck = "e2fsck"
917     _fsckErrors = {4: _("File system errors left uncorrected."),
918     8: _("Operational error."),
919     16: _("Usage or syntax error."),
920     32: _("e2fsck cancelled by user request."),
921     128: _("Shared library error.")}
922     _packages = ["e2fsprogs"]
923     _formattable = True
924     _supported = True
925     _resizable = True
926     _bootable = True
927     _linuxNative = True
928     _maxSize = 8 * 1024 * 1024
929     _minSize = 0
930     _defaultFormatOptions = []
931     _defaultMountOptions = ["defaults"]
932     _defaultCheckOptions = ["-f", "-p", "-C", "0"]
933     _dump = True
934     _check = True
935     _migratable = True
936     _migrationTarget = "ext3"
937     _migratefs = "tune2fs"
938     _defaultMigrateOptions = ["-j"]
939     _infofs = "dumpe2fs"
940     _defaultInfoOptions = ["-h"]
941     _existingSizeFields = ["Block count:", "Block size:"]
942     _fsProfileSpecifier = "-T"
943     partedSystem = fileSystemType["ext2"]
944    
945     def _fsckFailed(self, rc):
946     for errorCode in self._fsckErrors.keys():
947     if rc & errorCode:
948     return True
949     return False
950    
951     def _fsckErrorMessage(self, rc):
952     msg = ''
953    
954     for errorCode in self._fsckErrors.keys():
955     if rc & errorCode:
956     msg += "\n" + self._fsckErrors[errorCode]
957    
958     return msg.strip()
959    
960     def doMigrate(self, intf=None):
961     FS.doMigrate(self, intf=intf)
962     self.tuneFS()
963    
964     def doFormat(self, *args, **kwargs):
965     FS.doFormat(self, *args, **kwargs)
966     self.tuneFS()
967    
968     def tuneFS(self):
969     if not isys.ext2HasJournal(self.device):
970     # only do this if there's a journal
971     return
972    
973     try:
974     rc = iutil.execWithRedirect("tune2fs",
975     ["-c0", "-i0",
976     "-ouser_xattr,acl", self.device],
977     stdout = "/dev/tty5",
978     stderr = "/dev/tty5")
979     except Exception as e:
980     log.error("failed to run tune2fs on %s: %s" % (self.device, e))
981    
982     @property
983     def minSize(self):
984     """ Minimum size for this filesystem in MB. """
985     if self._minInstanceSize is None:
986     # try once in the beginning to get the minimum size for an
987     # existing filesystem.
988     size = self._minSize
989     blockSize = None
990    
991     if self.exists and os.path.exists(self.device):
992     # get block size
993     buf = iutil.execWithCapture(self.infofsProg,
994     ["-h", self.device],
995     stderr="/dev/tty5")
996     for line in buf.splitlines():
997     if line.startswith("Block size:"):
998     blockSize = int(line.split(" ")[-1])
999     break
1000    
1001     if blockSize is None:
1002     raise FSError("failed to get block size for %s filesystem "
1003     "on %s" % (self.mountType, self.device))
1004    
1005     # get minimum size according to resize2fs
1006     buf = iutil.execWithCapture(self.resizefsProg,
1007     ["-P", self.device],
1008     stderr="/dev/tty5")
1009     for line in buf.splitlines():
1010     if "minimum size of the filesystem:" not in line:
1011     continue
1012    
1013     # line will look like:
1014     # Estimated minimum size of the filesystem: 1148649
1015     #
1016     # NOTE: The minimum size reported is in blocks. Convert
1017     # to bytes, then megabytes, and finally round up.
1018     (text, sep, minSize) = line.partition(": ")
1019     size = long(minSize) * blockSize
1020     size = math.ceil(size / 1024.0 / 1024.0)
1021     break
1022    
1023     if size is None:
1024     log.warning("failed to get minimum size for %s filesystem "
1025     "on %s" % (self.mountType, self.device))
1026    
1027     self._minInstanceSize = size
1028    
1029     return self._minInstanceSize
1030    
1031     @property
1032     def isDirty(self):
1033     return isys.ext2IsDirty(self.device)
1034    
1035     @property
1036     def resizeArgs(self):
1037     argv = ["-p", self.device, "%dM" % (self.targetSize,)]
1038     return argv
1039    
1040     register_device_format(Ext2FS)
1041    
1042    
1043     class Ext3FS(Ext2FS):
1044     """ ext3 filesystem. """
1045     _type = "ext3"
1046     _defaultFormatOptions = ["-t", "ext3"]
1047     _migratable = False
1048     _modules = ["ext3"]
1049     _defaultMigrateOptions = ["-O", "extents"]
1050     partedSystem = fileSystemType["ext3"]
1051    
1052     # It is possible for a user to specify an fsprofile that defines a blocksize
1053     # smaller than the default of 4096 bytes and therefore to make liars of us
1054     # with regard to this maximum filesystem size, but if they're doing such
1055     # things they should know the implications of their chosen block size.
1056     _maxSize = 16 * 1024 * 1024
1057    
1058     register_device_format(Ext3FS)
1059    
1060    
1061     class Ext4FS(Ext3FS):
1062     """ ext4 filesystem. """
1063     _type = "ext4"
1064     _defaultFormatOptions = ["-t", "ext4"]
1065     _migratable = False
1066     _modules = ["ext4"]
1067     partedSystem = fileSystemType["ext4"]
1068    
1069     register_device_format(Ext4FS)
1070    
1071    
1072     class FATFS(FS):
1073     """ FAT filesystem. """
1074     _type = "vfat"
1075     _mkfs = "mkdosfs"
1076     _modules = ["vfat"]
1077     _labelfs = "dosfslabel"
1078     _fsck = "dosfsck"
1079     _fsckErrors = {1: _("Recoverable errors have been detected or dosfsck has "
1080     "discovered an internal inconsistency."),
1081     2: _("Usage error.")}
1082     _supported = True
1083     _formattable = True
1084     _maxSize = 1024 * 1024
1085     _packages = [ "dosfstools" ]
1086     _defaultMountOptions = ["umask=0077", "shortname=winnt"]
1087     # FIXME this should be fat32 in some cases
1088     partedSystem = fileSystemType["fat16"]
1089    
1090     def _fsckFailed(self, rc):
1091     if rc >= 1:
1092     return True
1093     return False
1094    
1095     def _fsckErrorMessage(self, rc):
1096     return self._fsckErrors[rc]
1097    
1098     register_device_format(FATFS)
1099    
1100    
1101     class EFIFS(FATFS):
1102     _type = "efi"
1103     _mountType = "vfat"
1104     _modules = ["vfat"]
1105     _name = "EFI System Partition"
1106     _minSize = 50
1107     _bootable = True
1108    
1109     @property
1110     def supported(self):
1111     import platform
1112     p = platform.getPlatform(None)
1113     return (isinstance(p, platform.EFI) and
1114     p.isEfi and
1115     self.utilsAvailable)
1116    
1117     register_device_format(EFIFS)
1118    
1119    
1120     class BTRFS(FS):
1121     """ btrfs filesystem """
1122     _type = "btrfs"
1123     _mkfs = "mkfs.btrfs"
1124     _modules = ["btrfs"]
1125     _resizefs = "btrfsctl"
1126     _formattable = True
1127     _linuxNative = True
1128     _bootable = False
1129     _maxLabelChars = 256
1130     _supported = False
1131     _dump = True
1132     _check = True
1133     _packages = ["btrfs-progs"]
1134     _maxSize = 16 * 1024 * 1024
1135     # FIXME parted needs to be thaught about btrfs so that we can set the
1136     # partition table type correctly for btrfs partitions
1137     # partedSystem = fileSystemType["btrfs"]
1138    
1139     def _getFormatOptions(self, options=None):
1140     argv = []
1141     if options and isinstance(options, list):
1142     argv.extend(options)
1143     argv.extend(self.defaultFormatOptions)
1144     if self.label:
1145     argv.extend(["-L", self.label])
1146     argv.append(self.device)
1147     return argv
1148    
1149     @property
1150     def resizeArgs(self):
1151     argv = ["-r", "%dm" % (self.targetSize,), self.device]
1152     return argv
1153    
1154     @property
1155     def supported(self):
1156     """ Is this filesystem a supported type? """
1157     supported = self._supported
1158     if flags.cmdline.has_key("btrfs"):
1159     supported = self.utilsAvailable
1160    
1161     return supported
1162    
1163     register_device_format(BTRFS)
1164    
1165    
1166     class GFS2(FS):
1167     """ gfs2 filesystem. """
1168     _type = "gfs2"
1169     _mkfs = "mkfs.gfs2"
1170     _modules = ["dlm", "gfs2"]
1171     _formattable = True
1172     _defaultFormatOptions = ["-j", "1", "-p", "lock_nolock", "-O"]
1173     _linuxNative = True
1174     _supported = False
1175     _dump = True
1176     _check = True
1177     _packages = ["gfs2-utils"]
1178     # FIXME parted needs to be thaught about btrfs so that we can set the
1179     # partition table type correctly for btrfs partitions
1180     # partedSystem = fileSystemType["gfs2"]
1181    
1182     @property
1183     def supported(self):
1184     """ Is this filesystem a supported type? """
1185     supported = self._supported
1186     if flags.cmdline.has_key("gfs2"):
1187     supported = self.utilsAvailable
1188    
1189     return supported
1190    
1191     register_device_format(GFS2)
1192    
1193    
1194     class JFS(FS):
1195     """ JFS filesystem """
1196     _type = "jfs"
1197     _mkfs = "mkfs.jfs"
1198     _modules = ["jfs"]
1199     _labelfs = "jfs_tune"
1200     _defaultFormatOptions = ["-q"]
1201     _defaultLabelOptions = ["-L"]
1202     _maxLabelChars = 16
1203     _maxSize = 8 * 1024 * 1024
1204     _formattable = True
1205     _linuxNative = True
1206     _supported = False
1207     _dump = True
1208     _check = True
1209     _infofs = "jfs_tune"
1210     _defaultInfoOptions = ["-l"]
1211     _existingSizeFields = ["Aggregate block size:", "Aggregate size:"]
1212     partedSystem = fileSystemType["jfs"]
1213    
1214     @property
1215     def supported(self):
1216     """ Is this filesystem a supported type? """
1217     supported = self._supported
1218     if flags.cmdline.has_key("jfs"):
1219     supported = self.utilsAvailable
1220    
1221     return supported
1222    
1223     register_device_format(JFS)
1224    
1225    
1226     class ReiserFS(FS):
1227     """ reiserfs filesystem """
1228     _type = "reiserfs"
1229     _mkfs = "mkreiserfs"
1230     _resizefs = "resize_reiserfs"
1231     _labelfs = "reiserfstune"
1232     _modules = ["reiserfs"]
1233     _defaultFormatOptions = ["-f", "-f"]
1234     _defaultLabelOptions = ["-l"]
1235     _maxLabelChars = 16
1236     _maxSize = 16 * 1024 * 1024
1237     _formattable = True
1238     _linuxNative = True
1239     _supported = False
1240     _dump = True
1241     _check = True
1242     _packages = ["reiserfs-utils"]
1243     _infofs = "debugreiserfs"
1244     _defaultInfoOptions = []
1245     _existingSizeFields = ["Count of blocks on the device:", "Blocksize:"]
1246     partedSystem = fileSystemType["reiserfs"]
1247    
1248     @property
1249     def supported(self):
1250     """ Is this filesystem a supported type? """
1251     supported = self._supported
1252     if flags.cmdline.has_key("reiserfs"):
1253     supported = self.utilsAvailable
1254    
1255     return supported
1256    
1257     @property
1258     def resizeArgs(self):
1259     argv = ["-s", "%dM" % (self.targetSize,), self.device]
1260     return argv
1261    
1262     register_device_format(ReiserFS)
1263    
1264    
1265     class XFS(FS):
1266     """ XFS filesystem """
1267     _type = "xfs"
1268     _mkfs = "mkfs.xfs"
1269     _modules = ["xfs"]
1270     _labelfs = "xfs_admin"
1271     _defaultFormatOptions = ["-f"]
1272     _defaultLabelOptions = ["-L"]
1273     _maxLabelChars = 16
1274     _maxSize = 16 * 1024 * 1024
1275     _formattable = True
1276     _linuxNative = True
1277     _supported = True
1278     _dump = True
1279     _check = True
1280     _packages = ["xfsprogs"]
1281     _infofs = "xfs_db"
1282     _defaultInfoOptions = ["-c", "\"sb 0\"", "-c", "\"p dblocks\"",
1283     "-c", "\"p blocksize\""]
1284     _existingSizeFields = ["dblocks =", "blocksize ="]
1285     partedSystem = fileSystemType["xfs"]
1286    
1287     def _getLabelArgs(self, label):
1288     argv = []
1289     argv.extend(self.defaultLabelOptions)
1290     argv.extend([label, self.device])
1291     return argv
1292    
1293     register_device_format(XFS)
1294    
1295    
1296     class HFS(FS):
1297     _type = "hfs"
1298     _mkfs = "hformat"
1299     _modules = ["hfs"]
1300     _formattable = True
1301     partedSystem = fileSystemType["hfs"]
1302    
1303     register_device_format(HFS)
1304    
1305    
1306     class AppleBootstrapFS(HFS):
1307     _type = "appleboot"
1308     _mountType = "hfs"
1309     _name = "Apple Bootstrap"
1310     _bootable = True
1311     _minSize = 800.00 / 1024.00
1312     _maxSize = 1
1313    
1314     @property
1315     def supported(self):
1316     import platform
1317     return (isinstance(platform.getPlatform(None), platform.NewWorldPPC)
1318     and self.utilsAvailable)
1319    
1320     def writeKS(self, f):
1321     f.write("appleboot --fstype=%s" % self.type)
1322    
1323     register_device_format(AppleBootstrapFS)
1324    
1325    
1326     # this doesn't need to be here
1327     class HFSPlus(FS):
1328     _type = "hfs+"
1329     _modules = ["hfsplus"]
1330     _udevTypes = ["hfsplus"]
1331     partedSystem = fileSystemType["hfs+"]
1332    
1333     register_device_format(HFSPlus)
1334    
1335    
1336     class NTFS(FS):
1337     """ ntfs filesystem. """
1338     _type = "ntfs"
1339     _resizefs = "ntfsresize"
1340     _fsck = "ntfsresize"
1341     _resizable = True
1342     _minSize = 1
1343     _maxSize = 16 * 1024 * 1024
1344     _defaultMountOptions = ["defaults", "ro"]
1345     _defaultCheckOptions = ["-c"]
1346     _packages = ["ntfsprogs"]
1347     _infofs = "ntfsinfo"
1348     _defaultInfoOptions = ["-m"]
1349     _existingSizeFields = ["Cluster Size:", "Volume Size in Clusters:"]
1350     partedSystem = fileSystemType["ntfs"]
1351    
1352     def _fsckFailed(self, rc):
1353     if rc != 0:
1354     return True
1355     return False
1356    
1357     @property
1358     def minSize(self):
1359     """ The minimum filesystem size in megabytes. """
1360     if self._minInstanceSize is None:
1361     # we try one time to determine the minimum size.
1362     size = self._minSize
1363     if self.exists and os.path.exists(self.device):
1364     minSize = None
1365     buf = iutil.execWithCapture(self.resizefsProg,
1366     ["-m", self.device],
1367     stderr = "/dev/tty5")
1368     for l in buf.split("\n"):
1369     if not l.startswith("Minsize"):
1370     continue
1371     try:
1372     min = l.split(":")[1].strip()
1373     minSize = int(min) + 250
1374     except Exception, e:
1375     minSize = None
1376     log.warning("Unable to parse output for minimum size on %s: %s" %(self.device, e))
1377    
1378     if minSize is None:
1379     log.warning("Unable to discover minimum size of filesystem "
1380     "on %s" %(self.device,))
1381     else:
1382     size = minSize
1383    
1384     self._minInstanceSize = size
1385    
1386     return self._minInstanceSize
1387    
1388     @property
1389     def resizeArgs(self):
1390     # You must supply at least two '-f' options to ntfsresize or
1391     # the proceed question will be presented to you.
1392     argv = ["-ff", "-s", "%dM" % (self.targetSize,), self.device]
1393     return argv
1394    
1395    
1396     register_device_format(NTFS)
1397    
1398    
1399     # if this isn't going to be mountable it might as well not be here
1400     class NFS(FS):
1401     """ NFS filesystem. """
1402     _type = "nfs"
1403     _modules = ["nfs"]
1404    
1405     def _deviceCheck(self, devspec):
1406     if devspec is not None and ":" not in devspec:
1407     raise ValueError("device must be of the form <host>:<path>")
1408    
1409     @property
1410     def mountable(self):
1411     return False
1412    
1413     def _setDevice(self, devspec):
1414     self._deviceCheck(devspec)
1415     self._device = devspec
1416    
1417     def _getDevice(self):
1418     return self._device
1419    
1420     device = property(lambda f: f._getDevice(),
1421     lambda f,d: f._setDevice(d),
1422     doc="Full path the device this format occupies")
1423    
1424     register_device_format(NFS)
1425    
1426    
1427     class NFSv4(NFS):
1428     """ NFSv4 filesystem. """
1429     _type = "nfs4"
1430     _modules = ["nfs4"]
1431    
1432     register_device_format(NFSv4)
1433    
1434    
1435     class Iso9660FS(FS):
1436     """ ISO9660 filesystem. """
1437     _type = "iso9660"
1438     _formattable = False
1439     _supported = True
1440     _resizable = False
1441     _bootable = False
1442     _linuxNative = False
1443     _dump = False
1444     _check = False
1445     _migratable = False
1446     _defaultMountOptions = ["ro"]
1447    
1448     def writeKS(self, f):
1449     return
1450    
1451     register_device_format(Iso9660FS)
1452    
1453    
1454     class NoDevFS(FS):
1455     """ nodev filesystem base class """
1456     _type = "nodev"
1457    
1458     def __init__(self, *args, **kwargs):
1459     FS.__init__(self, *args, **kwargs)
1460     self.exists = True
1461     self.device = self.type
1462    
1463     def _setDevice(self, devspec):
1464     self._device = devspec
1465    
1466     def _getExistingSize(self):
1467     pass
1468    
1469     def writeKS(self, f):
1470     return
1471    
1472     register_device_format(NoDevFS)
1473    
1474    
1475     class DevPtsFS(NoDevFS):
1476     """ devpts filesystem. """
1477     _type = "devpts"
1478     _defaultMountOptions = ["gid=5", "mode=620"]
1479    
1480     register_device_format(DevPtsFS)
1481    
1482    
1483     # these don't really need to be here
1484     class ProcFS(NoDevFS):
1485     _type = "proc"
1486    
1487     register_device_format(ProcFS)
1488    
1489    
1490     class SysFS(NoDevFS):
1491     _type = "sysfs"
1492    
1493     register_device_format(SysFS)
1494    
1495    
1496     class TmpFS(NoDevFS):
1497     _type = "tmpfs"
1498    
1499     register_device_format(TmpFS)
1500    
1501    
1502     class BindFS(FS):
1503     _type = "bind"
1504    
1505     @property
1506     def mountable(self):
1507     return True
1508    
1509     def _getExistingSize(self):
1510     pass
1511    
1512     def writeKS(self, f):
1513     return
1514    
1515     register_device_format(BindFS)
1516    
1517    

admin@koozali.org
ViewVC Help
Powered by ViewVC 1.2.1 RSS 2.0 feed