blob: 8b85027247bcbda004ec3fc1f1c204eb56944801 [file] [log] [blame]
{-# LANGUAGE FlexibleContexts #-}
{-| Implementation of functions specific to configuration management.
-}
{-
Copyright (C) 2014 Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-}
module Ganeti.WConfd.ConfigVerify
( verifyConfig
, verifyConfigErr
) where
import Control.Monad.Error
import qualified Data.Foldable as F
import qualified Data.Map as M
import qualified Data.Set as S
import Ganeti.Errors
import Ganeti.JSON (GenericContainer(..), Container)
import Ganeti.Objects
import Ganeti.Types
import Ganeti.Utils
import Ganeti.Utils.Validate
-- * Configuration checks
-- | A helper function that returns the key set of a container.
keysSet :: (Ord k) => GenericContainer k v -> S.Set k
keysSet = M.keysSet . fromContainer
-- | Checks that all objects are indexed by their proper UUID.
checkUUIDKeys :: (UuidObject a, Show a)
=> String -> Container a -> ValidationMonad ()
checkUUIDKeys what = mapM_ check . M.toList . fromContainer
where
check (uuid, x) = reportIf (uuid /= uuidOf x)
$ what ++ " '" ++ show x
++ "' is indexed by wrong UUID '" ++ uuid ++ "'"
-- | Checks that all linked UUID of given objects exist.
checkUUIDRefs :: (UuidObject a, Show a, F.Foldable f)
=> String -> String
-> (a -> [String]) -> f a -> Container b
-> ValidationMonad ()
checkUUIDRefs whatObj whatTarget linkf xs targets = F.mapM_ check xs
where
uuids = keysSet targets
check x = forM_ (linkf x) $ \uuid ->
reportIf (not $ S.member uuid uuids)
$ whatObj ++ " '" ++ show x ++ "' references a non-existing "
++ whatTarget ++ " UUID '" ++ uuid ++ "'"
-- | Checks consistency of a given configuration.
--
-- TODO: Currently this implements only some very basic checks.
-- Evenually all checks from Python ConfigWriter need to be moved here
-- (see issue #759).
verifyConfig :: ConfigData -> ValidationMonad ()
verifyConfig cd = do
let cluster = configCluster cd
nodes = configNodes cd
nodegroups = configNodegroups cd
instances = configInstances cd
networks = configNetworks cd
disks = configDisks cd
-- global cluster checks
let enabledHvs = clusterEnabledHypervisors cluster
hvParams = clusterHvparams cluster
reportIf (null enabledHvs)
"enabled hypervisors list doesn't have any entries"
-- we don't need to check for invalid HVS as they would fail to parse
let missingHvp = S.fromList enabledHvs S.\\ keysSet hvParams
reportIf (not $ S.null missingHvp)
$ "hypervisor parameters missing for the enabled hypervisor(s) "
++ (commaJoin . map hypervisorToRaw . S.toList $ missingHvp)
let enabledDiskTemplates = clusterEnabledDiskTemplates cluster
reportIf (null enabledDiskTemplates)
"enabled disk templates list doesn't have any entries"
-- we don't need to check for invalid templates as they wouldn't parse
let masterNodeName = clusterMasterNode cluster
reportIf (not $ masterNodeName `S.member` keysSet (configNodes cd))
$ "cluster has invalid primary node " ++ masterNodeName
-- UUIDs
checkUUIDKeys "node" nodes
checkUUIDKeys "nodegroup" nodegroups
checkUUIDKeys "instances" instances
checkUUIDKeys "network" networks
checkUUIDKeys "disk" disks
-- UUID references
checkUUIDRefs "node" "nodegroup" (return . nodeGroup) nodes nodegroups
checkUUIDRefs "instance" "primary node" (maybe [] return . instPrimaryNode)
instances nodes
checkUUIDRefs "instance" "disks" instDisks instances disks
-- | Checks consistency of a given configuration.
-- If there is an error, throw 'ConfigVerifyError'.
verifyConfigErr :: (MonadError GanetiException m) => ConfigData -> m ()
verifyConfigErr cd =
case runValidate $ verifyConfig cd of
(_, []) -> return ()
(_, es) -> throwError $ ConfigVerifyError "Validation failed" es