[master 3/4] Cache data obtained from /proc/self/mountinfo.
mulkieran
installerbot-noreply at redhat.com
Tue May 12 12:32:45 UTC 2015
From: mulhern <amulhern at redhat.com>
This avoids reading and parsing the file whenever additional
btrfs information needs to be looked up.
Changes are:
* Addition of a small, special purpose cache for btrfs information
obtained from mountinfo. The only facility the cache offers is laziness.
Since it currently is instantiated and destroyed in a single method where
it is used in a very simple way it does not require other management
facilities.
* Slightly improved parsing of /proc/self/mountinfo which takes into
account some variable number fields.
* Make sure all components of key values are str or NoneType when
inserted into mounts cache or when looked up.
* Use context manager for "/proc/mounts" processing.
* Log an error if a line in "/proc/mounts" is skipped because no
subvolspec was obtained for the btrfs volume entry.
* Use named constant MAIN_VOLUME_ID instead of 5 where appropriate.
Signed-off-by: mulhern <amulhern at redhat.com>
---
blivet/mounts.py | 93 ++++++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 71 insertions(+), 22 deletions(-)
diff --git a/blivet/mounts.py b/blivet/mounts.py
index 617ca21..a7763cf 100644
--- a/blivet/mounts.py
+++ b/blivet/mounts.py
@@ -21,10 +21,59 @@
#
from collections import defaultdict
from . import util
+from .devicelibs import btrfs
import logging
log = logging.getLogger("blivet")
+class _MountinfoCache(object):
+ """ Cache for info from /proc/self/mountinfo. Looks up the root of the
+ mount within the filesystem using a pair of mountpoint, mount
+ source as keys.
+
+ This is a very simple helper class for MountsCache, and would
+ have to be altered for general purpose use.
+
+ Note that only info for fstype btrfs is stored within the cache,
+ as MountsCache methods only require information for btrfs.
+ """
+
+ def __init__(self):
+ self._cache = None
+
+ def _getCache(self):
+ """ Reads lines in /proc/self/mountinfo and builds a table. """
+ cache = {}
+
+ with open("/proc/self/mountinfo") as mountinfos:
+ for line in mountinfos:
+ fields = line.split()
+ separator_index = fields.index("-")
+ fstype = fields[separator_index + 1]
+ if not fstype.startswith("btrfs"):
+ continue
+
+ root = fields[3]
+ mountpoint = fields[4]
+ devspec = fields[separator_index + 2]
+ cache[(devspec, mountpoint)] = root
+
+ return cache
+
+ def getRoot(self, devspec, mountpoint):
+ """ Retrieves the root of the mount within the filesystem
+ that corresponds to devspec and mountpoint.
+
+ :param str devspec: device specification
+ :param str mountpoint: mountpoint
+ :rtype: str or NoneType
+ :returns: the root of the mount within the filesystem, if available
+ """
+ if self._cache is None:
+ self._cache = self._getCache()
+
+ return self._cache.get((devspec, mountpoint))
+
class MountsCache(object):
""" Cache object for system mountpoints; checks /proc/mounts and
/proc/self/mountinfo for up-to-date information.
@@ -40,7 +89,7 @@ def getMountpoints(self, devspec, subvolspec=None):
:param devscpec: device specification, eg. "/dev/vda1"
:type devspec: str
:param subvolspec: btrfs subvolume specification, eg. ID or name
- :type subvolspec: str
+ :type subvolspec: object (may be NoneType)
:returns: list of mountpoints (path)
:rtype: list of str or empty list
@@ -50,6 +99,9 @@ def getMountpoints(self, devspec, subvolspec=None):
"""
self._cacheCheck()
+ if subvolspec is not None:
+ subvolspec = str(subvolspec)
+
return self.mountpoints[(devspec, subvolspec)]
def isMountpoint(self, path):
@@ -68,28 +120,25 @@ def _getActiveMounts(self):
Refreshes self.mountpoints with current mountpoint information
"""
self.mountpoints = defaultdict(list)
- for line in open("/proc/mounts").readlines():
- try:
- (devspec, mountpoint, fstype, _rest) = line.split(None, 3)
- except ValueError:
- log.error("failed to parse /proc/mounts line: %s", line)
- continue
-
- if fstype == "btrfs":
- # get the subvol name from /proc/self/mountinfo
- for line in open("/proc/self/mountinfo").readlines():
- fields = line.split()
- _subvol = fields[3]
- _mountpoint = fields[4]
- _devspec = fields[9]
- if _mountpoint == mountpoint and _devspec == devspec:
- # empty _subvol[1:] means it is a top-level volume
- subvolspec = _subvol[1:] or 5
-
+ mountinfo = _MountinfoCache()
+
+ with open("/proc/mounts") as mounts:
+ for line in mounts:
+ try:
+ (devspec, mountpoint, fstype, _rest) = line.split(None, 3)
+ except ValueError:
+ log.error("failed to parse /proc/mounts line: %s", line)
+ continue
+
+ if fstype == "btrfs":
+ root = mountinfo.getRoot(devspec, mountpoint)
+ if root is not None:
+ subvolspec = root[1:] or str(btrfs.MAIN_VOLUME_ID)
self.mountpoints[(devspec, subvolspec)].append(mountpoint)
-
- else:
- self.mountpoints[(devspec, None)].append(mountpoint)
+ else:
+ log.error("failed to obtain subvolspec for btrfs device %s at mountpoint %s", devspec, mountpoint)
+ else:
+ self.mountpoints[(devspec, None)].append(mountpoint)
def _cacheCheck(self):
""" Computes the MD5 hash on /proc/mounts and updates the cache on change
--
To view this commit on github, visit https://github.com/rhinstaller/blivet/commit/854d12a5fdcde323ba8a095014265097c466350f
More information about the anaconda-patches
mailing list