Add support for creating tmpfs mounts, mounting them and adding them to fstab.
Tmpfs is a filesystem that lives in the Kernel page cache and stores data primarily in RAM and in swap once RAM runs out.
Data stored on tmpfs mounts will not survive a system reboot/crash/shutdown.
About tmpfs mount size: - if no size is specified, the size will be 50% by default - if size is specified, the mount will have this size -> there is no limit on the size on tmpfs mounts -> just note that once it fills RAM (and any swap), the system will grind to a halt - grow and maxsize are supported -> just using grow without maxsize will set the size to 100% RAM -> if maxsize is > RAM, the size will be 100% RAM -> if maxize is < RAM, the size will be a corresponding percentage of RAM
Signed-off-by: Martin Kolman mkolman@redhat.com --- blivet/__init__.py | 4 ++ blivet/deviceaction.py | 7 ++- blivet/devices.py | 24 ++++++++++ blivet/formats/fs.py | 125 ++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 157 insertions(+), 3 deletions(-)
diff --git a/blivet/__init__.py b/blivet/__init__.py index d15a0d0..afd5a49 100644 --- a/blivet/__init__.py +++ b/blivet/__init__.py @@ -1192,6 +1192,10 @@ class Blivet(object): kwargs["subvol"] = True return self.newBTRFS(*args, **kwargs)
+ def newTmpFS(self, *args, **kwargs): + """ Return a new TmpFSDevice. """ + return TmpFSDevice(*args, **kwargs) + def createDevice(self, device): """ Schedule creation of a device.
diff --git a/blivet/deviceaction.py b/blivet/deviceaction.py index 202a66c..7160cce 100644 --- a/blivet/deviceaction.py +++ b/blivet/deviceaction.py @@ -473,8 +473,11 @@ class ActionCreateFormat(DeviceAction): udev_settle() self.device.updateSysfsPath() info = udev_get_block_device(self.device.sysfsPath) - self.device.format.uuid = udev_device_get_uuid(info) - self.device.deviceLinks = udev_device_get_symlinks(info) + # only do this if the format has a device known to udev + # (the format might not have a normal device at all) + if info: + self.device.format.uuid = udev_device_get_uuid(info) + self.device.deviceLinks = udev_device_get_symlinks(info)
def cancel(self): self.device.format = self.origFormat diff --git a/blivet/devices.py b/blivet/devices.py index 6650086..c278433 100644 --- a/blivet/devices.py +++ b/blivet/devices.py @@ -3760,6 +3760,30 @@ class NoDevice(StorageDevice): self._preDestroy()
+class TmpFSDevice(NoDevice): + def __init__(self, *args, **kwargs): + """Create a tmpfs device""" + format = kwargs.get('format') + NoDevice.__init__(self, format) + # the tmpfs device does not exist until mounted + self.exists = False + self._size = kwargs["size"] + self._targetSize = self._size + + @property + def size(self): + if self._size is not None: + return self._size + elif self.format: + return self.format.size + else: + return None + + @property + def fstabSpec(self): + return "tmpfs" + + class FileDevice(StorageDevice): """ A file on a filesystem.
diff --git a/blivet/formats/fs.py b/blivet/formats/fs.py index 13896c9..f7e1da8 100644 --- a/blivet/formats/fs.py +++ b/blivet/formats/fs.py @@ -1406,7 +1406,7 @@ class NoDevFS(FS): def __init__(self, *args, **kwargs): FS.__init__(self, *args, **kwargs) self.exists = True - self.device = self.type + self.device = self._type
def _setDevice(self, devspec): self._device = devspec @@ -1451,6 +1451,129 @@ register_device_format(SysFS)
class TmpFS(NoDevFS): _type = "tmpfs" + _supported = True + _resizable = True + # as tmpfs is part of the Linux kernel, + # I guess it is linux-native + _linuxNative = True + # empty tmpfs has zero overhead + _minSize = 0 + _minInstanceSize = 0 + # tmpfs really does not occupy any space by itself + _minSize = 0 + # in a sense, I guess tmpfs is formatable + # in the regard that the format is atuomatically created + # once mounted + _formattable = True + + def __init__(self, *args, **kwargs): + NoDevFS.__init__(self, *args, **kwargs) + self.exists = True + self._device = "tmpfs" + + # check if fixed filesystem size has been specified, + # if no size is specified, tmpfs will by default + # be limited to half of the system RAM + # (sizes of all tmpfs mounts are independent) + fsoptions = kwargs.get("mountopts") + grow = kwargs.get("grow") + maxsize = kwargs.get("maxsize") + size_option = "" + if fsoptions: + self._apendOptions(fsoptions) + if self._size: + self._apendOptions("size=%dm" % self._size) + elif grow: + # grow to 100% RAM + size_option = "size=100%%" + system_ram = util.total_memory()/1024 # kB to MB + fs_size = system_ram + if maxsize: + # check if maxsize is < RAM + if maxsize < system_ram: + # as maxsize is smaller than RAM, filesystem size = maxsize + fs_size = maxsize + # maxsize is less than size of RAM + # -> convert to % of RAM + size_fraction = maxsize/float(system_ram) + # "size=%1.0f%%" % (0.5555*100) -> "size=56%" + size_option = "size=%1.0f%%" % (size_fraction*100) + self._size = fs_size + kwargs["size"] = fs_size + + if size_option: + self._apendOptions(size_option) + + def mount(self, *args, **kwargs): + """The filesystem doesn't need to be separately mounted""" + pass + + def create(self, *arg, **kwargs): + """A filesystem is created automatically once tmpfs is mounted""" + pass + + @property + def mountable(self): + return True + + def _getOptions(self): + if self._options: + return self._options + else: + return "defaults" + + def _setOptions(self, options): + self._options = options + + # override the options property + # so that the size and other options + # are correctly added to fstab + options = property(_getOptions, _setOptions) + + @property + def size(self): + return self._size + + @property + def minSize(self): + """ The minimum filesystem size in megabytes. """ + return self._minInstanceSize + + @property + def free(self): + free_space = 0 + if self.mountpoint: + # we need both the output and the return code + rc, buf = util._run_program(['df', '-P', self.mountpoint]) + # XXXX, what about changeroot ? + if rc == 0: + lines = buf.readlines() + if len(lines) == 2: # header and a single data row + space_data = lines[0].split(" ") + if len(space_data) >= 6: + free_space = int(space_data[3]) + # convert to MB + free_space = math.ceil(size / 1024.0) + return free_space + + def _setDevice(self, devspec): + self._device = devspec + + def _getDevice(self): + return self._device + + device = property(lambda f: f._getDevice(), + lambda f,d: f._setDevice(d), + doc="Full path the device this format occupies") + + def _apendOptions(self, new_options): + """Append free form option string + to the options (used among others in fstab) + """ + if self._options: # append to existing options + self._options = "%s,%s" % (self._options, new_options) + else: # not yet set, just assign it + self._options = new_options
register_device_format(TmpFS)