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

Reply via email to