1 {-| Implementation of the Ganeti Query2 node queries.
7 Copyright (C) 2012 Google Inc.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 module Ganeti.Query.Node
31 import Control.Applicative
33 import qualified Data.Map as Map
34 import qualified Text.JSON as J
40 import Ganeti.Query.Language
41 import Ganeti.Query.Common
42 import Ganeti.Query.Types
44 -- | NodeRuntime is the resulting type for NodeInfo call.
45 type NodeRuntime = Either RpcError RpcResultNodeInfo
47 -- | List of node live fields.
48 nodeLiveFieldsDefs :: [(FieldName, FieldTitle, FieldType, String, FieldDoc)]
50 [ ("bootid", "BootID", QFTText, "bootid",
51 "Random UUID renewed for each system reboot, can be used\
52 \ for detecting reboots by tracking changes")
53 , ("cnodes", "CNodes", QFTNumber, "cpu_nodes",
54 "Number of NUMA domains on node (if exported by hypervisor)")
55 , ("csockets", "CSockets", QFTNumber, "cpu_sockets",
56 "Number of physical CPU sockets (if exported by hypervisor)")
57 , ("ctotal", "CTotal", QFTNumber, "cpu_total",
58 "Number of logical processors")
59 , ("dfree", "DFree", QFTUnit, "vg_free",
60 "Available disk space in volume group")
61 , ("dtotal", "DTotal", QFTUnit, "vg_size",
62 "Total disk space in volume group used for instance disk allocation")
63 , ("mfree", "MFree", QFTUnit, "memory_free",
64 "Memory available for instance allocations")
65 , ("mnode", "MNode", QFTUnit, "memory_dom0",
66 "Amount of memory used by node (dom0 for Xen)")
67 , ("mtotal", "MTotal", QFTUnit, "memory_total",
68 "Total amount of memory of physical machine")
71 -- | Map each name to a function that extracts that value from
73 nodeLiveFieldExtract :: String -> RpcResultNodeInfo -> J.JSValue
74 nodeLiveFieldExtract "bootid" res =
75 J.showJSON $ rpcResNodeInfoBootId res
76 nodeLiveFieldExtract "cpu_nodes" res =
77 jsonHead (rpcResNodeInfoHvInfo res) hvInfoCpuNodes
78 nodeLiveFieldExtract "cpu_sockets" res =
79 jsonHead (rpcResNodeInfoHvInfo res) hvInfoCpuSockets
80 nodeLiveFieldExtract "cpu_total" res =
81 jsonHead (rpcResNodeInfoHvInfo res) hvInfoCpuTotal
82 nodeLiveFieldExtract "vg_free" res =
83 jsonHead (rpcResNodeInfoVgInfo res) vgInfoVgFree
84 nodeLiveFieldExtract "vg_size" res =
85 jsonHead (rpcResNodeInfoVgInfo res) vgInfoVgSize
86 nodeLiveFieldExtract "memory_free" res =
87 jsonHead (rpcResNodeInfoHvInfo res) hvInfoMemoryFree
88 nodeLiveFieldExtract "memory_dom0" res =
89 jsonHead (rpcResNodeInfoHvInfo res) hvInfoMemoryDom0
90 nodeLiveFieldExtract "memory_total" res =
91 jsonHead (rpcResNodeInfoHvInfo res) hvInfoMemoryTotal
92 nodeLiveFieldExtract _ _ = J.JSNull
94 -- | Helper for extracting field from RPC result.
95 nodeLiveRpcCall :: FieldName -> NodeRuntime -> Node -> ResultEntry
96 nodeLiveRpcCall fname (Right res) _ =
97 rsNormal (nodeLiveFieldExtract fname res)
98 nodeLiveRpcCall _ (Left err) _ =
99 ResultEntry (rpcErrorToStatus err) Nothing
101 -- | Builder for node live fields.
102 nodeLiveFieldBuilder :: (FieldName, FieldTitle, FieldType, String, FieldDoc)
103 -> FieldData Node NodeRuntime
104 nodeLiveFieldBuilder (fname, ftitle, ftype, _, fdoc) =
105 ( FieldDefinition fname ftitle ftype fdoc
106 , FieldRuntime $ nodeLiveRpcCall fname)
108 -- | The docstring for the node role. Note that we use 'reverse in
109 -- order to keep the same order as Python.
110 nodeRoleDoc :: String
115 "\"" ++ nodeRoleToRaw role ++ "\" for " ++ roleDescription role)
116 (reverse [minBound..maxBound]))
118 -- | List of all node fields.
119 nodeFields :: FieldList Node NodeRuntime
121 [ (FieldDefinition "drained" "Drained" QFTBool "Whether node is drained",
122 FieldSimple (rsNormal . nodeDrained))
123 , (FieldDefinition "master_candidate" "MasterC" QFTBool
124 "Whether node is a master candidate",
125 FieldSimple (rsNormal . nodeMasterCandidate))
126 , (FieldDefinition "master_capable" "MasterCapable" QFTBool
127 "Whether node can become a master candidate",
128 FieldSimple (rsNormal . nodeMasterCapable))
129 , (FieldDefinition "name" "Node" QFTText "Node name",
130 FieldSimple (rsNormal . nodeName))
131 , (FieldDefinition "offline" "Offline" QFTBool
132 "Whether node is marked offline",
133 FieldSimple (rsNormal . nodeOffline))
134 , (FieldDefinition "vm_capable" "VMCapable" QFTBool
135 "Whether node can host instances",
136 FieldSimple (rsNormal . nodeVmCapable))
137 , (FieldDefinition "pip" "PrimaryIP" QFTText "Primary IP address",
138 FieldSimple (rsNormal . nodePrimaryIp))
139 , (FieldDefinition "sip" "SecondaryIP" QFTText "Secondary IP address",
140 FieldSimple (rsNormal . nodeSecondaryIp))
141 , (FieldDefinition "master" "IsMaster" QFTBool "Whether node is master",
142 FieldConfig (\cfg node ->
143 rsNormal (nodeName node ==
144 clusterMasterNode (configCluster cfg))))
145 , (FieldDefinition "group" "Group" QFTText "Node group",
146 FieldConfig (\cfg node ->
147 rsMaybe (groupName <$> getGroupOfNode cfg node)))
148 , (FieldDefinition "group.uuid" "GroupUUID" QFTText "UUID of node group",
149 FieldSimple (rsNormal . nodeGroup))
150 , (FieldDefinition "ndparams" "NodeParameters" QFTOther
151 "Merged node parameters",
152 FieldConfig ((rsMaybe .) . getNodeNdParams))
153 , (FieldDefinition "custom_ndparams" "CustomNodeParameters" QFTOther
154 "Custom node parameters",
155 FieldSimple (rsNormal . nodeNdparams))
156 -- FIXME: the below could be generalised a bit, like in Python
157 , (FieldDefinition "pinst_cnt" "Pinst" QFTNumber
158 "Number of instances with this node as primary",
160 rsNormal . length . fst . getNodeInstances cfg . nodeName))
161 , (FieldDefinition "sinst_cnt" "Sinst" QFTNumber
162 "Number of instances with this node as secondary",
164 rsNormal . length . snd . getNodeInstances cfg . nodeName))
165 , (FieldDefinition "pinst_list" "PriInstances" QFTOther
166 "List of instances with this node as primary",
167 FieldConfig (\cfg -> rsNormal . map instName . fst .
168 getNodeInstances cfg . nodeName))
169 , (FieldDefinition "sinst_list" "SecInstances" QFTOther
170 "List of instances with this node as secondary",
171 FieldConfig (\cfg -> rsNormal . map instName . snd .
172 getNodeInstances cfg . nodeName))
173 , (FieldDefinition "role" "Role" QFTText nodeRoleDoc,
174 FieldConfig ((rsNormal .) . getNodeRole))
175 -- FIXME: the powered state is special (has an different context,
176 -- not runtime) in Python
177 , (FieldDefinition "powered" "Powered" QFTBool
178 "Whether node is thought to be powered on",
180 -- FIXME: the two fields below are incomplete in Python, part of the
181 -- non-implemented node resource model; they are declared just for
182 -- parity, but are not functional
183 , (FieldDefinition "hv_state" "HypervisorState" QFTOther "Hypervisor state",
185 , (FieldDefinition "disk_state" "DiskState" QFTOther "Disk state",
188 map nodeLiveFieldBuilder nodeLiveFieldsDefs ++
189 map buildNdParamField allNDParamFields ++
192 serialFields "Node" ++
195 -- | The node fields map.
196 nodeFieldsMap :: FieldMap Node NodeRuntime
197 nodeFieldsMap = Map.fromList $ map (\v -> (fdefName (fst v), v)) nodeFields