import Control.Applicative
import Data.List
+import Data.Maybe
import qualified Data.Map as Map
import qualified Text.JSON as J
import Ganeti.Config
+import Ganeti.Logging
import Ganeti.Objects
import Ganeti.JSON
import Ganeti.Rpc
import Ganeti.Query.Language
import Ganeti.Query.Common
import Ganeti.Query.Types
+import Ganeti.Utils (niceSort)
-- | Runtime is the resulting type for NodeInfo call.
type Runtime = Either RpcError RpcResultNodeInfo
, FieldRuntime $ nodeLiveRpcCall fname
, QffNormal)
--- | The docstring for the node role. Note that we use 'reverse in
+-- | The docstring for the node role. Note that we use 'reverse' in
-- order to keep the same order as Python.
nodeRoleDoc :: String
nodeRoleDoc =
"Whether node can become a master candidate",
FieldSimple (rsNormal . nodeMasterCapable), QffNormal)
, (FieldDefinition "name" "Node" QFTText "Node name",
- FieldSimple (rsNormal . nodeName), QffNormal)
+ FieldSimple (rsNormal . nodeName), QffHostname)
, (FieldDefinition "offline" "Offline" QFTBool
"Whether node is marked offline",
FieldSimple (rsNormal . nodeOffline), QffNormal)
QffNormal)
, (FieldDefinition "pinst_list" "PriInstances" QFTOther
"List of instances with this node as primary",
- FieldConfig (\cfg -> rsNormal . map instName . fst .
+ FieldConfig (\cfg -> rsNormal . niceSort . map instName . fst .
getNodeInstances cfg . nodeName), QffNormal)
, (FieldDefinition "sinst_list" "SecInstances" QFTOther
"List of instances with this node as secondary",
- FieldConfig (\cfg -> rsNormal . map instName . snd .
+ FieldConfig (\cfg -> rsNormal . niceSort . map instName . snd .
getNodeInstances cfg . nodeName), QffNormal)
, (FieldDefinition "role" "Role" QFTText nodeRoleDoc,
FieldConfig ((rsNormal .) . getNodeRole), QffNormal)
-- non-implemented node resource model; they are declared just for
-- parity, but are not functional
, (FieldDefinition "hv_state" "HypervisorState" QFTOther "Hypervisor state",
- missingRuntime, QffNormal)
+ FieldSimple (const rsUnavail), QffNormal)
, (FieldDefinition "disk_state" "DiskState" QFTOther "Disk state",
- missingRuntime, QffNormal)
+ FieldSimple (const rsUnavail), QffNormal)
] ++
map nodeLiveFieldBuilder nodeLiveFieldsDefs ++
map buildNdParamField allNDParamFields ++
fieldsMap =
Map.fromList $ map (\v@(f, _, _) -> (fdefName f, v)) nodeFields
+-- | Scan the list of results produced by executeRpcCall and log all the RPC
+-- errors.
+logRpcErrors :: [(Node, Runtime)] -> IO ()
+logRpcErrors allElems =
+ let logOneRpcErr (_, Right _) = return ()
+ logOneRpcErr (_, Left err) =
+ logError $ "Error in the RPC HTTP reply: " ++ show err
+ in mapM_ logOneRpcErr allElems
+
-- | Collect live data from RPC query if enabled.
--
-- FIXME: Check which fields we actually need and possibly send empty
collectLiveData False _ nodes =
return $ zip nodes (repeat $ Left (RpcResultError "Live data disabled"))
collectLiveData True cfg nodes = do
- let vgs = [clusterVolumeGroupName $ configCluster cfg]
+ let vgs = maybeToList . clusterVolumeGroupName $ configCluster cfg
hvs = [getDefaultHypervisor cfg]
step n (bn, gn, em) =
let ndp' = getNodeNdParams cfg n
Nothing -> (n : bn, gn, em)
(bnodes, gnodes, emap) = foldr step ([], [], []) nodes
rpcres <- executeRpcCall gnodes (RpcCallNodeInfo vgs hvs (Map.fromList emap))
+ logRpcErrors rpcres
-- FIXME: The order of nodes in the result could be different from the input
return $ zip bnodes (repeat $ Left (RpcResultError "Broken configuration"))
++ rpcres