Some paths, such as /bin or /usr/lib, should not be used for file
storage. This patch implements a check during cluster verification
to show a warning in case such a path has been used.
---
lib/backend.py | 6 +++
lib/cmdlib.py | 74 ++++++++++++++++++++++++++++++++++++++++
lib/constants.py | 4 ++
test/ganeti.cmdlib_unittest.py | 20 +++++++++++
4 files changed, 104 insertions(+), 0 deletions(-)
diff --git a/lib/backend.py b/lib/backend.py
index 4a87fdb..a4e7c9a 100644
--- a/lib/backend.py
+++ b/lib/backend.py
@@ -797,6 +797,12 @@ def VerifyNode(what, cluster_name):
result[constants.NV_BRIDGES] = [bridge
for bridge in what[constants.NV_BRIDGES]
if not utils.BridgeExists(bridge)]
+
+ if what.get(constants.NV_FILE_STORAGE_PATHS) == my_name:
+ result[constants.NV_FILE_STORAGE_PATHS] = \
+ (pathutils.FILE_STORAGE_PATHS_FILE,
+ bdev.LoadAllowedFileStoragePaths(pathutils.FILE_STORAGE_PATHS_FILE))
+
return result
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 5e6d5e9..aee786a 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -1783,6 +1783,48 @@ def _VerifyCertificate(filename):
raise errors.ProgrammerError("Unhandled certificate error code %r" % errcode)
+def _GetFileStorageWarningPaths():
+ """Builds a list of path prefixes which shouldn't be used for file storage.
+
+ """
+ paths = set([
+ "/boot",
+ "/dev",
+ "/etc",
+ "/home",
+ "/proc",
+ "/root",
+ "/sys",
+ ])
+
+ for prefix in ["", "/usr", "/usr/local"]:
+ paths.update(map(lambda s: "%s/%s" % (prefix, s),
+ ["bin", "lib", "lib32", "lib64", "sbin"]))
+
+ return frozenset(map(os.path.normpath, paths))
+
+
+def _CheckAllowedFileStoragePaths(paths, _warn=_GetFileStorageWarningPaths()):
+ """Cross-checks a list of paths for prefixes considered critical.
+
+ Some paths, e.g. "/bin", should not be used for file storage.
+
+ @type paths: list
+ @param paths: List of paths to be checked
+ @rtype: list
+ @return: Sorted list of paths for which the user should be warned
+
+ """
+ clean_paths = map(os.path.normpath, paths)
+
+ result = [path
+ for path in clean_paths
+ for warn_path in _warn
+ if warn_path == path or utils.IsBelowDir(warn_path, path)]
+
+ return utils.NiceSort(result)
+
+
def _GetAllHypervisorParameters(cluster, instances):
"""Compute the set of all hypervisor parameters.
@@ -2768,6 +2810,32 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors):
"OSes present on reference node %s but missing on this node: %s",
base.name, utils.CommaJoin(missing))
+ def _VerifyFileStoragePaths(self, ninfo, nresult, is_master):
+ """Verifies paths in L{pathutils.FILE_STORAGE_PATHS_FILE}.
+
+ @type ninfo: L{objects.Node}
+ @param ninfo: the node to check
+ @param nresult: the remote results for the node
+ @type is_master: bool
+ @param is_master: Whether node is the master node
+
+ """
+ node = ninfo.name
+
+ if is_master:
+ (filename_on_node, allowed_paths) = \
+ nresult[constants.NV_FILE_STORAGE_PATHS]
+
+ warn = _CheckAllowedFileStoragePaths(allowed_paths)
+
+ self._ErrorIf(warn, constants.CV_ENODEFILESTORAGEPATHS, node,
+ ("File '%s' allows file storage paths which shouldn't"
+ " be used: %s"),
+ filename_on_node, utils.CommaJoin(warn),
+ code=self.ETYPE_WARNING)
+ else:
+ assert constants.NV_FILE_STORAGE_PATHS not in nresult
+
def _VerifyOob(self, ninfo, nresult):
"""Verifies out of band functionality of a node.
@@ -3110,6 +3178,10 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors):
node_verify_param[constants.NV_DRBDLIST] = None
node_verify_param[constants.NV_DRBDHELPER] = drbd_helper
+ if constants.ENABLE_FILE_STORAGE or constants.ENABLE_SHARED_FILE_STORAGE:
+ # Load file storage paths only from master node
+ node_verify_param[constants.NV_FILE_STORAGE_PATHS] = master_node
+
# bridge checks
# FIXME: this needs to be changed per node-group, not cluster-wide
bridges = set()
@@ -3263,6 +3335,8 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors):
self._VerifyNodeNetwork(node_i, nresult)
self._VerifyNodeUserScripts(node_i, nresult)
self._VerifyOob(node_i, nresult)
+ self._VerifyFileStoragePaths(node_i, nresult,
+ node == master_node)
if nimg.vm_capable:
self._VerifyNodeLVM(node_i, nresult, vg_name)
diff --git a/lib/constants.py b/lib/constants.py
index 1d9787c..59fd225 100644
--- a/lib/constants.py
+++ b/lib/constants.py
@@ -1326,6 +1326,8 @@ CV_ENODEOOBPATH = \
(CV_TNODE, "ENODEOOBPATH", "Invalid Out Of Band path")
CV_ENODEUSERSCRIPTS = \
(CV_TNODE, "ENODEUSERSCRIPTS", "User scripts not present or not executable")
+CV_ENODEFILESTORAGEPATHS = \
+ (CV_TNODE, "ENODEFILESTORAGEPATHS", "Detected bad file storage paths")
CV_ALL_ECODES = frozenset([
CV_ECLUSTERCFG,
@@ -1359,6 +1361,7 @@ CV_ALL_ECODES = frozenset([
CV_ENODETIME,
CV_ENODEOOBPATH,
CV_ENODEUSERSCRIPTS,
+ CV_ENODEFILESTORAGEPATHS,
])
CV_ALL_ECODES_STRINGS = frozenset(estr for (_, estr, _) in CV_ALL_ECODES)
@@ -1385,6 +1388,7 @@ NV_VMNODES = "vmnodes"
NV_OOB_PATHS = "oob-paths"
NV_BRIDGES = "bridges"
NV_USERSCRIPTS = "user-scripts"
+NV_FILE_STORAGE_PATHS = "file-storage-paths"
# Instance status
INSTST_RUNNING = "running"
diff --git a/test/ganeti.cmdlib_unittest.py b/test/ganeti.cmdlib_unittest.py
index ab5ef6f..7128b58 100755
--- a/test/ganeti.cmdlib_unittest.py
+++ b/test/ganeti.cmdlib_unittest.py
@@ -1478,5 +1478,25 @@ class TestDiskSizeInBytesToMebibytes(unittest.TestCase):
self.assertEqual(warnsize, (1024 * 1024) - j)
+class TestCheckAllowedFileStoragePaths(unittest.TestCase):
+ def testPaths(self):
+ warn_paths = cmdlib._GetFileStorageWarningPaths()
+
+ for path in ["/bin", "/usr/local/sbin", "/lib64", "/etc"]:
+ self.assertTrue(path in warn_paths)
+
+ self.assertEqual(set(map(os.path.normpath, warn_paths)),
+ warn_paths)
+
+ def test(self):
+ cafsp = cmdlib._CheckAllowedFileStoragePaths
+ self.assertEqual(cafsp([]), [])
+ self.assertEqual(cafsp(["/tmp"]), [])
+ self.assertEqual(cafsp(["/bin/ls"]), ["/bin/ls"])
+ self.assertEqual(cafsp(["/bin"]), ["/bin"])
+ self.assertEqual(cafsp(["/usr/sbin/vim", "/srv/file-storage"]),
+ ["/usr/sbin/vim"])
+
+
if __name__ == "__main__":
testutils.GanetiTestProgram()
--
1.7.7.3