Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / Query / Common.hs @ e86c9deb

History | View | Annotate | Download (8.8 kB)

1
{-| Implementation of the Ganeti Query2 common objects.
2

    
3
 -}
4

    
5
{-
6

    
7
Copyright (C) 2012, 2013 Google Inc.
8

    
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.
13

    
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.
18

    
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
22
02110-1301, USA.
23

    
24
-}
25

    
26
module Ganeti.Query.Common
27
  ( NoDataRuntime(..)
28
  , rsNoData
29
  , rsUnavail
30
  , rsNormal
31
  , rsMaybeNoData
32
  , rsMaybeUnavail
33
  , rsErrorNoData
34
  , rsUnknown
35
  , missingRuntime
36
  , rpcErrorToStatus
37
  , timeStampFields
38
  , uuidFields
39
  , serialFields
40
  , tagsFields
41
  , dictFieldGetter
42
  , buildNdParamField
43
  , buildBeParamField
44
  , buildHvParamField
45
  , getDefaultHypervisorSpec
46
  , getHvParamsFromCluster
47
  , aliasFields
48
  ) where
49

    
50
import Control.Monad (guard)
51
import qualified Data.Map as Map
52
import Data.Maybe (fromMaybe)
53
import Text.JSON (JSON, showJSON)
54

    
55
import Ganeti.BasicTypes
56
import qualified Ganeti.Constants as C
57
import Ganeti.Config
58
import Ganeti.Errors
59
import Ganeti.JSON
60
import Ganeti.Objects
61
import Ganeti.Rpc
62
import Ganeti.Query.Language
63
import Ganeti.Query.Types
64
import Ganeti.Types
65

    
66
-- | The runtime used by queries which retrieve no live data.
67
data NoDataRuntime = NoDataRuntime
68

    
69
-- * Generic functions
70

    
71
-- | Conversion from 'VType' to 'FieldType'.
72
vTypeToQFT :: VType -> FieldType
73
vTypeToQFT VTypeString      = QFTOther
74
vTypeToQFT VTypeMaybeString = QFTOther
75
vTypeToQFT VTypeBool        = QFTBool
76
vTypeToQFT VTypeSize        = QFTUnit
77
vTypeToQFT VTypeInt         = QFTNumber
78

    
79
-- * Result helpers
80

    
81
-- | Helper for a result with no data.
82
rsNoData :: ResultEntry
83
rsNoData = ResultEntry RSNoData Nothing
84

    
85
-- | Helper for result for an entity which supports no such field.
86
rsUnavail :: ResultEntry
87
rsUnavail = ResultEntry RSUnavail Nothing
88

    
89
-- | Helper to declare a normal result.
90
rsNormal :: (JSON a) => a -> ResultEntry
91
rsNormal a = ResultEntry RSNormal $ Just (showJSON a)
92

    
93
-- | Helper to declare a result from a 'Maybe' (the item might be
94
-- missing, in which case we return no data). Note that there's some
95
-- ambiguity here: in some cases, we mean 'RSNoData', but in other
96
-- 'RSUnavail'; this is easy to solve in simple cases, but not in
97
-- nested dicts. If you want to return 'RSUnavail' in case of 'Nothing'
98
-- use the function 'rsMaybeUnavail'.
99
rsMaybeNoData :: (JSON a) => Maybe a -> ResultEntry
100
rsMaybeNoData = maybe rsNoData rsNormal
101

    
102
-- | Helper to declare a result from a 'ErrorResult' (an error happened
103
-- while retrieving the data from a config, or there was no data).
104
-- This function should be used if an error signals there was no data.
105
rsErrorNoData :: (JSON a) => ErrorResult a -> ResultEntry
106
rsErrorNoData res = case res of
107
  Ok  x -> rsNormal x
108
  Bad _ -> rsNoData
109

    
110
-- | Helper to declare a result from a 'Maybe'. This version returns
111
-- a 'RSUnavail' in case of 'Nothing'. It should be used for optional
112
-- fields that are not set. For cases where 'Nothing' means that there
113
-- was an error, consider using 'rsMaybe' instead.
114
rsMaybeUnavail :: (JSON a) => Maybe a -> ResultEntry
115
rsMaybeUnavail = maybe rsUnavail rsNormal
116

    
117
-- | Helper for unknown field result.
118
rsUnknown :: ResultEntry
119
rsUnknown = ResultEntry RSUnknown Nothing
120

    
121
-- | Helper for a missing runtime parameter.
122
missingRuntime :: FieldGetter a b
123
missingRuntime = FieldRuntime (\_ _ -> ResultEntry RSNoData Nothing)
124

    
125
-- * Error conversion
126

    
127
-- | Convert RpcError to ResultStatus
128
rpcErrorToStatus :: RpcError -> ResultStatus
129
rpcErrorToStatus OfflineNodeError = RSOffline
130
rpcErrorToStatus _ = RSNoData
131

    
132
-- * Common fields
133

    
134
-- | The list of timestamp fields.
135
timeStampFields :: (TimeStampObject a) => FieldList a b
136
timeStampFields =
137
  [ (FieldDefinition "ctime" "CTime" QFTTimestamp "Creation timestamp",
138
     FieldSimple (rsNormal . cTimeOf), QffNormal)
139
  , (FieldDefinition "mtime" "MTime" QFTTimestamp "Modification timestamp",
140
     FieldSimple (rsNormal . mTimeOf), QffNormal)
141
  ]
142

    
143
-- | The list of UUID fields.
144
uuidFields :: (UuidObject a) => String -> FieldList a b
145
uuidFields name =
146
  [ (FieldDefinition "uuid" "UUID" QFTText  (name ++ " UUID"),
147
     FieldSimple (rsNormal . uuidOf), QffNormal) ]
148

    
149
-- | The list of serial number fields.
150
serialFields :: (SerialNoObject a) => String -> FieldList a b
151
serialFields name =
152
  [ (FieldDefinition "serial_no" "SerialNo" QFTNumber
153
     (name ++ " object serial number, incremented on each modification"),
154
     FieldSimple (rsNormal . serialOf), QffNormal) ]
155

    
156
-- | The list of tag fields.
157
tagsFields :: (TagsObject a) => FieldList a b
158
tagsFields =
159
  [ (FieldDefinition "tags" "Tags" QFTOther "Tags",
160
     FieldSimple (rsNormal . tagsOf), QffNormal) ]
161

    
162
-- * Generic parameter functions
163

    
164
-- | Returns a field from a (possibly missing) 'DictObject'. This is
165
-- used by parameter dictionaries, usually. Note that we have two
166
-- levels of maybe: the top level dict might be missing, or one key in
167
-- the dictionary might be.
168
dictFieldGetter :: (DictObject a) => String -> Maybe a -> ResultEntry
169
dictFieldGetter k = maybe rsNoData (rsMaybeNoData . lookup k . toDict)
170

    
171
-- | Ndparams optimised lookup map.
172
ndParamTypes :: Map.Map String FieldType
173
ndParamTypes = Map.map vTypeToQFT C.ndsParameterTypes
174

    
175
-- | Ndparams title map.
176
ndParamTitles :: Map.Map String FieldTitle
177
ndParamTitles = C.ndsParameterTitles
178

    
179
-- | Ndparam getter builder: given a field, it returns a FieldConfig
180
-- getter, that is a function that takes the config and the object and
181
-- returns the Ndparam field specified when the getter was built.
182
ndParamGetter :: (NdParamObject a) =>
183
                 String -- ^ The field we're building the getter for
184
              -> ConfigData -> a -> ResultEntry
185
ndParamGetter field config =
186
  dictFieldGetter field . getNdParamsOf config
187

    
188
-- | Builds the ndparam fields for an object.
189
buildNdParamField :: (NdParamObject a) => String -> FieldData a b
190
buildNdParamField =
191
  buildParamField "ndp" "node" ndParamTitles ndParamTypes ndParamGetter
192

    
193
-- | Beparams optimised lookup map.
194
beParamTypes :: Map.Map String FieldType
195
beParamTypes = Map.map vTypeToQFT C.besParameterTypes
196

    
197
-- | Builds the beparam fields for an object.
198
buildBeParamField :: (String -> ConfigData -> a -> ResultEntry)
199
                  -> String
200
                  -> FieldData a b
201
buildBeParamField =
202
  buildParamField "be" "backend" C.besParameterTitles beParamTypes
203

    
204
-- | Hvparams optimised lookup map.
205
hvParamTypes :: Map.Map String FieldType
206
hvParamTypes = Map.map vTypeToQFT C.hvsParameterTypes
207

    
208
-- | Builds the beparam fields for an object.
209
buildHvParamField :: (String -> ConfigData -> a -> ResultEntry)
210
                  -> String
211
                  -> FieldData a b
212
buildHvParamField =
213
  buildParamField "hv" "hypervisor" C.hvsParameterTitles hvParamTypes
214

    
215
-- | Builds a param field for a certain getter class
216
buildParamField :: String -- ^ Prefix
217
                -> String -- ^ Parameter group name
218
                -> Map.Map String String -- ^ Parameter title map
219
                -> Map.Map String FieldType -- ^ Parameter type map
220
                -> (String -> ConfigData -> a -> ResultEntry)
221
                -> String -- ^ The parameter name
222
                -> FieldData a b
223
buildParamField prefix paramGroupName titleMap typeMap getter field =
224
  let full_name = prefix ++ "/" ++ field
225
      title = fromMaybe full_name $ field `Map.lookup` titleMap
226
      qft = fromMaybe QFTOther $ field `Map.lookup` typeMap
227
      desc = "The \"" ++ field ++ "\" " ++ paramGroupName ++ " parameter"
228
  in ( FieldDefinition full_name title qft desc
229
     , FieldConfig (getter field), QffNormal
230
     )
231

    
232
-- | Looks up the default hypervisor and its hvparams
233
getDefaultHypervisorSpec :: ConfigData -> (Hypervisor, HvParams)
234
getDefaultHypervisorSpec cfg = (hv, getHvParamsFromCluster cfg hv)
235
  where hv = getDefaultHypervisor cfg
236

    
237
-- | Looks up the cluster's hvparams of the given hypervisor
238
getHvParamsFromCluster :: ConfigData -> Hypervisor -> HvParams
239
getHvParamsFromCluster cfg hv =
240
  fromMaybe (GenericContainer Map.empty) .
241
    Map.lookup (hypervisorToRaw hv) .
242
      fromContainer . clusterHvparams $ configCluster cfg
243

    
244
-- | Given an alias list and a field list, copies field definitions under a
245
-- new field name. Aliases should be tested - see the test module
246
-- 'Test.Ganeti.Query.Aliases'!
247
aliasFields :: [(FieldName, FieldName)] -> FieldList a b -> FieldList a b
248
aliasFields aliases fieldList = fieldList ++ do
249
  alias <- aliases
250
  (FieldDefinition name d1 d2 d3, v1, v2) <- fieldList
251
  guard (snd alias == name)
252
  return (FieldDefinition (fst alias) d1 d2 d3, v1, v2)