Commit 9c1c3c19 authored by Helga Velroyen's avatar Helga Velroyen

Verify file storage path

This patch adds two verification steps to 'gnt-cluster
verify':
- The configured file storage directory is checked against
  the allowed file storage directories file.
- We check whether the configured file storage directory
  is existing and writable on each node.
Signed-off-by: default avatarHelga Velroyen <helgav@google.com>
Reviewed-by: default avatarKlaus Aehlig <aehlig@google.com>
parent 13a6c760
......@@ -1134,6 +1134,12 @@ def VerifyNode(what, cluster_name, all_hvparams):
result[constants.NV_ACCEPTED_STORAGE_PATHS] = \
filestorage.ComputeWrongFileStoragePaths()
if what.get(constants.NV_FILE_STORAGE_PATH):
pathresult = filestorage.CheckFileStoragePath(
what[constants.NV_FILE_STORAGE_PATH])
if pathresult:
result[constants.NV_FILE_STORAGE_PATH] = pathresult
return result
......
......@@ -2324,6 +2324,22 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors):
"Node should not have returned forbidden file storage"
" paths")
def _VerifyStoragePaths(self, ninfo, nresult):
"""Verifies (file) storage paths.
@type ninfo: L{objects.Node}
@param ninfo: the node to check
@param nresult: the remote results for the node
"""
cluster = self.cfg.GetClusterInfo()
if cluster.IsFileStorageEnabled():
self._ErrorIf(
constants.NV_FILE_STORAGE_PATH in nresult,
constants.CV_ENODEFILESTORAGEPATHUNUSABLE, ninfo.name,
"The configured file storage path is unusable: %s" %
nresult.get(constants.NV_FILE_STORAGE_PATH))
def _VerifyOob(self, ninfo, nresult):
"""Verifies out of band functionality of a node.
......@@ -2839,6 +2855,7 @@ class LUClusterVerifyGroup(LogicalUnit, _VerifyErrors):
self._VerifyOob(node_i, nresult)
self._VerifyAcceptedFileStoragePaths(node_i, nresult,
node_i.uuid == master_node_uuid)
self._VerifyStoragePaths(node_i, nresult)
if nimg.vm_capable:
self._UpdateVerifyNodeLVM(node_i, nresult, vg_name, nimg)
......
......@@ -1647,6 +1647,8 @@ CV_ENODEUSERSCRIPTS = \
(CV_TNODE, "ENODEUSERSCRIPTS", "User scripts not present or not executable")
CV_ENODEFILESTORAGEPATHS = \
(CV_TNODE, "ENODEFILESTORAGEPATHS", "Detected bad file storage paths")
CV_ENODEFILESTORAGEPATHUNUSABLE = \
(CV_TNODE, "ENODEFILESTORAGEPATHUNUSABLE", "File storage path unusable")
CV_ALL_ECODES = compat.UniqueFrozenset([
CV_ECLUSTERCFG,
......@@ -1681,6 +1683,7 @@ CV_ALL_ECODES = compat.UniqueFrozenset([
CV_ENODEOOBPATH,
CV_ENODEUSERSCRIPTS,
CV_ENODEFILESTORAGEPATHS,
CV_ENODEFILESTORAGEPATHUNUSABLE,
])
CV_ALL_ECODES_STRINGS = \
......
......@@ -108,13 +108,16 @@ def ComputeWrongFileStoragePaths(_filename=pathutils.FILE_STORAGE_PATHS_FILE):
return _ComputeWrongFileStoragePaths(_LoadAllowedFileStoragePaths(_filename))
def _CheckFileStoragePath(path, allowed):
def _CheckFileStoragePath(path, allowed, exact_match_ok=False):
"""Checks if a path is in a list of allowed paths for file storage.
@type path: string
@param path: Path to check
@type allowed: list
@param allowed: List of allowed paths
@type exact_match_ok: bool
@param exact_match_ok: whether or not it is okay when the path is exactly
equal to an allowed path and not a subdir of it
@raise errors.FileStoragePathError: If the path is not allowed
"""
......@@ -127,6 +130,10 @@ def _CheckFileStoragePath(path, allowed):
logging.info("Ignoring relative path '%s' for file storage", i)
continue
if exact_match_ok:
if os.path.normpath(i) == os.path.normpath(path):
break
if utils.IsBelowDir(i, path):
break
else:
......@@ -150,7 +157,8 @@ def _LoadAllowedFileStoragePaths(filename):
def CheckFileStoragePathAcceptance(
path, _filename=pathutils.FILE_STORAGE_PATHS_FILE):
path, _filename=pathutils.FILE_STORAGE_PATHS_FILE,
exact_match_ok=False):
"""Checks if a path is allowed for file storage.
@type path: string
......@@ -164,4 +172,41 @@ def CheckFileStoragePathAcceptance(
raise errors.FileStoragePathError("Path '%s' uses a forbidden prefix" %
path)
_CheckFileStoragePath(path, allowed)
_CheckFileStoragePath(path, allowed, exact_match_ok=exact_match_ok)
def _CheckFileStoragePathExistance(path):
"""Checks whether the given path is usable on the file system.
This checks wether the path is existing, a directory and writable.
@type path: string
@param path: path to check
"""
if not os.path.isdir(path):
raise errors.FileStoragePathError("Path '%s' is not exisiting or not a"
" directory." % path)
if not os.access(path, os.W_OK):
raise errors.FileStoragePathError("Path '%s' is not writable" % path)
def CheckFileStoragePath(
path, _allowed_paths_file=pathutils.FILE_STORAGE_PATHS_FILE):
"""Checks whether the path exists and is acceptable to use.
@type path: string
@param path: path to check
@rtype: string
@returns: error message if the path is not ready to use
"""
try:
CheckFileStoragePathAcceptance(path, _filename=_allowed_paths_file,
exact_match_ok=True)
except errors.FileStoragePathError, e:
return e.message
if not os.path.isdir(path):
return "Path '%s' is not exisiting or not a directory." % path
if not os.access(path, os.W_OK):
return "Path '%s' is not writable" % path
......@@ -234,38 +234,39 @@ $(THH.makeJSONInstance ''VerifyOptionalChecks)
-- | Cluster verify error codes.
$(THH.declareSADT "CVErrorCode"
[ ("CvECLUSTERCFG", 'C.cvEclustercfgCode)
, ("CvECLUSTERCERT", 'C.cvEclustercertCode)
, ("CvECLUSTERFILECHECK", 'C.cvEclusterfilecheckCode)
, ("CvECLUSTERDANGLINGNODES", 'C.cvEclusterdanglingnodesCode)
, ("CvECLUSTERDANGLINGINST", 'C.cvEclusterdanglinginstCode)
, ("CvEINSTANCEBADNODE", 'C.cvEinstancebadnodeCode)
, ("CvEINSTANCEDOWN", 'C.cvEinstancedownCode)
, ("CvEINSTANCELAYOUT", 'C.cvEinstancelayoutCode)
, ("CvEINSTANCEMISSINGDISK", 'C.cvEinstancemissingdiskCode)
, ("CvEINSTANCEFAULTYDISK", 'C.cvEinstancefaultydiskCode)
, ("CvEINSTANCEWRONGNODE", 'C.cvEinstancewrongnodeCode)
, ("CvEINSTANCESPLITGROUPS", 'C.cvEinstancesplitgroupsCode)
, ("CvEINSTANCEPOLICY", 'C.cvEinstancepolicyCode)
, ("CvENODEDRBD", 'C.cvEnodedrbdCode)
, ("CvENODEDRBDHELPER", 'C.cvEnodedrbdhelperCode)
, ("CvENODEFILECHECK", 'C.cvEnodefilecheckCode)
, ("CvENODEHOOKS", 'C.cvEnodehooksCode)
, ("CvENODEHV", 'C.cvEnodehvCode)
, ("CvENODELVM", 'C.cvEnodelvmCode)
, ("CvENODEN1", 'C.cvEnoden1Code)
, ("CvENODENET", 'C.cvEnodenetCode)
, ("CvENODEOS", 'C.cvEnodeosCode)
, ("CvENODEORPHANINSTANCE", 'C.cvEnodeorphaninstanceCode)
, ("CvENODEORPHANLV", 'C.cvEnodeorphanlvCode)
, ("CvENODERPC", 'C.cvEnoderpcCode)
, ("CvENODESSH", 'C.cvEnodesshCode)
, ("CvENODEVERSION", 'C.cvEnodeversionCode)
, ("CvENODESETUP", 'C.cvEnodesetupCode)
, ("CvENODETIME", 'C.cvEnodetimeCode)
, ("CvENODEOOBPATH", 'C.cvEnodeoobpathCode)
, ("CvENODEUSERSCRIPTS", 'C.cvEnodeuserscriptsCode)
, ("CvENODEFILESTORAGEPATHS", 'C.cvEnodefilestoragepathsCode)
[ ("CvECLUSTERCFG", 'C.cvEclustercfgCode)
, ("CvECLUSTERCERT", 'C.cvEclustercertCode)
, ("CvECLUSTERFILECHECK", 'C.cvEclusterfilecheckCode)
, ("CvECLUSTERDANGLINGNODES", 'C.cvEclusterdanglingnodesCode)
, ("CvECLUSTERDANGLINGINST", 'C.cvEclusterdanglinginstCode)
, ("CvEINSTANCEBADNODE", 'C.cvEinstancebadnodeCode)
, ("CvEINSTANCEDOWN", 'C.cvEinstancedownCode)
, ("CvEINSTANCELAYOUT", 'C.cvEinstancelayoutCode)
, ("CvEINSTANCEMISSINGDISK", 'C.cvEinstancemissingdiskCode)
, ("CvEINSTANCEFAULTYDISK", 'C.cvEinstancefaultydiskCode)
, ("CvEINSTANCEWRONGNODE", 'C.cvEinstancewrongnodeCode)
, ("CvEINSTANCESPLITGROUPS", 'C.cvEinstancesplitgroupsCode)
, ("CvEINSTANCEPOLICY", 'C.cvEinstancepolicyCode)
, ("CvENODEDRBD", 'C.cvEnodedrbdCode)
, ("CvENODEDRBDHELPER", 'C.cvEnodedrbdhelperCode)
, ("CvENODEFILECHECK", 'C.cvEnodefilecheckCode)
, ("CvENODEHOOKS", 'C.cvEnodehooksCode)
, ("CvENODEHV", 'C.cvEnodehvCode)
, ("CvENODELVM", 'C.cvEnodelvmCode)
, ("CvENODEN1", 'C.cvEnoden1Code)
, ("CvENODENET", 'C.cvEnodenetCode)
, ("CvENODEOS", 'C.cvEnodeosCode)
, ("CvENODEORPHANINSTANCE", 'C.cvEnodeorphaninstanceCode)
, ("CvENODEORPHANLV", 'C.cvEnodeorphanlvCode)
, ("CvENODERPC", 'C.cvEnoderpcCode)
, ("CvENODESSH", 'C.cvEnodesshCode)
, ("CvENODEVERSION", 'C.cvEnodeversionCode)
, ("CvENODESETUP", 'C.cvEnodesetupCode)
, ("CvENODETIME", 'C.cvEnodetimeCode)
, ("CvENODEOOBPATH", 'C.cvEnodeoobpathCode)
, ("CvENODEUSERSCRIPTS", 'C.cvEnodeuserscriptsCode)
, ("CvENODEFILESTORAGEPATHS", 'C.cvEnodefilestoragepathsCode)
, ("CvENODEFILESTORAGEPATHUNUSABLE", 'C.cvEnodefilestoragepathunusableCode)
])
$(THH.makeJSONInstance ''CVErrorCode)
......
......@@ -21,7 +21,9 @@
"""Script for unittesting the ganeti.storage.file module"""
import os
import shutil
import tempfile
import unittest
from ganeti import errors
......@@ -43,7 +45,54 @@ class TestFileStorageSpaceInfo(unittest.TestCase):
"""Smoke test run on a directory that exists for sure.
"""
info = filestorage.GetFileStorageSpaceInfo("/")
filestorage.GetFileStorageSpaceInfo("/")
class TestCheckFileStoragePath(unittest.TestCase):
def _WriteAllowedFile(self, allowed_paths_filename, allowed_paths):
allowed_paths_file = open(allowed_paths_filename, 'w')
allowed_paths_file.write('\n'.join(allowed_paths))
allowed_paths_file.close()
def setUp(self):
self.tmpdir = tempfile.mkdtemp()
self.allowed_paths = [os.path.join(self.tmpdir, "allowed")]
for path in self.allowed_paths:
os.mkdir(path)
self.allowed_paths_filename = os.path.join(self.tmpdir, "allowed-path-file")
self._WriteAllowedFile(self.allowed_paths_filename, self.allowed_paths)
def tearDown(self):
shutil.rmtree(self.tmpdir)
def testCheckFileStoragePathExistance(self):
filestorage._CheckFileStoragePathExistance(self.tmpdir)
def testCheckFileStoragePathExistanceFail(self):
path = os.path.join(self.tmpdir, "does/not/exist")
self.assertRaises(errors.FileStoragePathError,
filestorage._CheckFileStoragePathExistance, path)
def testCheckFileStoragePathNotWritable(self):
path = os.path.join(self.tmpdir, "isnotwritable/")
os.mkdir(path)
os.chmod(path, 0)
self.assertRaises(errors.FileStoragePathError,
filestorage._CheckFileStoragePathExistance, path)
os.chmod(path, 777)
def testCheckFileStoragePath(self):
path = os.path.join(self.allowed_paths[0], "allowedsubdir")
os.mkdir(path)
result = filestorage.CheckFileStoragePath(
path, _allowed_paths_file=self.allowed_paths_filename)
self.assertEqual(None, result)
def testCheckFileStoragePathNotAllowed(self):
path = os.path.join(self.tmpdir, "notallowed")
result = filestorage.CheckFileStoragePath(
path, _allowed_paths_file=self.allowed_paths_filename)
self.assertTrue("not acceptable" in result)
if __name__ == "__main__":
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment