LGTM
On Tue, Jun 03, 2014 at 10:02:13AM +0200, 'Klaus Aehlig' via ganeti-devel wrote: > ...so that it can be used by other daemons as well. > > Signed-off-by: Klaus Aehlig <[email protected]> > --- > Makefile.am | 2 ++ > src/Ganeti/Daemon/Utils.hs | 85 > ++++++++++++++++++++++++++++++++++++++++++++++ > src/Ganeti/Query/Server.hs | 49 ++------------------------ > 3 files changed, 89 insertions(+), 47 deletions(-) > create mode 100644 src/Ganeti/Daemon/Utils.hs > > diff --git a/Makefile.am b/Makefile.am > index 10d8d27..ebd729b 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -122,6 +122,7 @@ HS_DIRS = \ > src/Ganeti/Curl \ > src/Ganeti/Cpu \ > src/Ganeti/DataCollectors \ > + src/Ganeti/Daemon \ > src/Ganeti/Hs2Py \ > src/Ganeti/HTools \ > src/Ganeti/HTools/Backend \ > @@ -749,6 +750,7 @@ HS_LIB_SRCS = \ > src/Ganeti/Cpu/Types.hs \ > src/Ganeti/Curl/Multi.hs \ > src/Ganeti/Daemon.hs \ > + src/Ganeti/Daemon/Utils.hs \ > src/Ganeti/DataCollectors/CLI.hs \ > src/Ganeti/DataCollectors/CPUload.hs \ > src/Ganeti/DataCollectors/Diskstats.hs \ > diff --git a/src/Ganeti/Daemon/Utils.hs b/src/Ganeti/Daemon/Utils.hs > new file mode 100644 > index 0000000..1f86e51 > --- /dev/null > +++ b/src/Ganeti/Daemon/Utils.hs > @@ -0,0 +1,85 @@ > +{-| Utility functions for complex operations carried out by several daemons. > + > +-} > + > +{- > + > +Copyright (C) 2014 Google Inc. > + > +This program is free software; you can redistribute it and/or modify > +it under the terms of the GNU General Public License as published by > +the Free Software Foundation; either version 2 of the License, or > +(at your option) any later version. > + > +This program is distributed in the hope that it will be useful, but > +WITHOUT ANY WARRANTY; without even the implied warranty of > +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > +General Public License for more details. > + > +You should have received a copy of the GNU General Public License > +along with this program; if not, write to the Free Software > +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA > +02110-1301, USA. > + > +-} > + > +module Ganeti.Daemon.Utils > + ( verifyMaster > + ) where > + > +import Control.Concurrent (threadDelay) > +import Control.Monad (unless) > +import Data.Either (rights) > +import qualified Data.Foldable as F > +import Data.List (partition) > + > +import Ganeti.BasicTypes > +import qualified Ganeti.Config as Config > +import qualified Ganeti.Constants as C > +import Ganeti.Daemon (getFQDN) > +import Ganeti.Logging > +import Ganeti.Objects > +import qualified Ganeti.Path as Path > +import Ganeti.Rpc > + > +-- | Gather votes from all nodes and verify that we we are > +-- the master. Return True if the voting is won, False if > +-- not enough > +verifyMasterVotes :: IO (Result Bool) > +verifyMasterVotes = runResultT $ do > + liftIO $ logDebug "Gathering votes for the master node" > + myName <- liftIO getFQDN > + liftIO . logDebug $ "My hostname is " ++ myName > + conf_file <- liftIO Path.clusterConfFile > + config <- mkResultT $ Config.loadConfig conf_file > + let nodes = F.toList $ configNodes config > + votes <- liftIO . executeRpcCall nodes $ RpcCallMasterNodeName > + let (missing, valid) = partition (isLeft . snd) votes > + noDataNodes = map (nodeName . fst) missing > + validVotes = map rpcResultMasterNodeNameMaster . rights $ map snd valid > + inFavor = length $ filter (== myName) validVotes > + voters = length nodes > + unknown = length missing > + liftIO . unless (null noDataNodes) . logWarning > + . (++) "No voting RPC result from " $ show noDataNodes > + liftIO . logDebug . (++) "Valid votes: " $ show validVotes > + if 2 * inFavor > voters > + then return True > + else if 2 * (inFavor + unknown) > voters > + then return False > + else fail $ "Voting cannot be won by " ++ myName > + ++ ", valid votes of " ++ show voters > + ++ " are " ++ show validVotes > + > +-- | Verify, by voting, that this node is the master. Bad if we're not. > +-- Allow the given number of retries to wait for not available nodes. > +verifyMaster :: Int -> IO (Result ()) > +verifyMaster retries = runResultT $ do > + won <- mkResultT verifyMasterVotes > + unless won $ > + if retries <= 0 > + then fail "Couldn't gather voting results of enough nodes" > + else do > + liftIO $ logDebug "Voting not final due to missing votes." > + liftIO . threadDelay $ C.masterVotingRetryIntervall * 1000000 > + mkResultT $ verifyMaster (retries - 1) > diff --git a/src/Ganeti/Query/Server.hs b/src/Ganeti/Query/Server.hs > index bf7ee97..70b1574 100644 > --- a/src/Ganeti/Query/Server.hs > +++ b/src/Ganeti/Query/Server.hs > @@ -32,17 +32,13 @@ module Ganeti.Query.Server > import Control.Applicative > import Control.Concurrent > import Control.Exception > -import Control.Monad (forever, when, mzero, guard, zipWithM, liftM, void, > - unless) > +import Control.Monad (forever, when, mzero, guard, zipWithM, liftM, void) > import Control.Monad.IO.Class > import Control.Monad.Trans (lift) > import Control.Monad.Trans.Maybe > import Data.Bits (bitSize) > -import Data.Either (rights) > -import qualified Data.Foldable as F > import qualified Data.Set as Set (toList) > import Data.IORef > -import Data.List (partition) > import Data.Maybe (fromMaybe) > import qualified Text.JSON as J > import Text.JSON (encode, showJSON, JSValue(..)) > @@ -56,6 +52,7 @@ import qualified Ganeti.ConstantUtils as ConstantUtils > (unFrozenSet) > import Ganeti.Errors > import qualified Ganeti.Path as Path > import Ganeti.Daemon > +import Ganeti.Daemon.Utils (verifyMaster) > import Ganeti.Objects > import qualified Ganeti.Config as Config > import Ganeti.ConfigReader > @@ -450,48 +447,6 @@ activateMasterIP = runResultT $ do > liftIO $ logDebug "finished activating master IP address" > return () > > --- | Gather votes from all nodes and verify that we we are > --- the master. Return True if the voting is won, False if > --- not enough > -verifyMasterVotes :: IO (Result Bool) > -verifyMasterVotes = runResultT $ do > - liftIO $ logDebug "Gathering votes for the master node" > - myName <- liftIO getFQDN > - liftIO . logDebug $ "My hostname is " ++ myName > - conf_file <- liftIO Path.clusterConfFile > - config <- mkResultT $ Config.loadConfig conf_file > - let nodes = F.toList $ configNodes config > - votes <- liftIO . executeRpcCall nodes $ RpcCallMasterNodeName > - let (missing, valid) = partition (isLeft . snd) votes > - noDataNodes = map (nodeName . fst) missing > - validVotes = map rpcResultMasterNodeNameMaster . rights $ map snd valid > - inFavor = length $ filter (== myName) validVotes > - voters = length nodes > - unknown = length missing > - liftIO . unless (null noDataNodes) . logWarning > - . (++) "No voting RPC result from " $ show noDataNodes > - liftIO . logDebug . (++) "Valid votes: " $ show validVotes > - if 2 * inFavor > voters > - then return True > - else if 2 * (inFavor + unknown) > voters > - then return False > - else fail $ "Voting cannot be won by " ++ myName > - ++ ", valid votes of " ++ show voters > - ++ " are " ++ show validVotes > - > --- | Verify, by voting, that this node is the master. Bad if we're not. > --- Allow the given number of retries to wait for not available nodes. > -verifyMaster :: Int -> IO (Result ()) > -verifyMaster retries = runResultT $ do > - won <- mkResultT verifyMasterVotes > - unless won $ > - if retries <= 0 > - then fail "Couldn't gather voting results of enough nodes" > - else do > - liftIO $ logDebug "Voting not final due to missing votes." > - liftIO . threadDelay $ C.masterVotingRetryIntervall * 1000000 > - mkResultT $ verifyMaster (retries - 1) > - > -- | Check function for luxid. > checkMain :: CheckFn () > checkMain opts = > -- > 1.9.1.423.g4596e3a >
