Revision c32c4e4d src/Ganeti/HTools/ExtLoader.hs

b/src/Ganeti/HTools/ExtLoader.hs
1
{-# LANGUAGE BangPatterns #-}
2

  
1 3
{-| External data loader.
2 4

  
3 5
This module holds the external data loading, and thus is the only one
......
31 33
  ( loadExternalData
32 34
  , commonSuffix
33 35
  , maybeSaveData
36
  , queryAllMonDDCs
34 37
  ) where
35 38

  
36 39
import Control.Monad
37 40
import Control.Exception
38
import Data.Maybe (isJust, fromJust)
41
import Data.Maybe (isJust, fromJust, catMaybes)
42
import Network.Curl
39 43
import System.FilePath
40 44
import System.IO
41 45
import System.Time (getClockTime)
42 46
import Text.Printf (hPrintf)
43 47

  
48
import qualified Text.JSON as J
49

  
50
import qualified Ganeti.Constants as C
51
import qualified Ganeti.DataCollectors.CPUload as CPUload
52
import qualified Ganeti.HTools.Container as Container
44 53
import qualified Ganeti.HTools.Backend.Luxi as Luxi
45 54
import qualified Ganeti.HTools.Backend.Rapi as Rapi
46 55
import qualified Ganeti.HTools.Backend.Simu as Simu
47 56
import qualified Ganeti.HTools.Backend.Text as Text
48 57
import qualified Ganeti.HTools.Backend.IAlloc as IAlloc
58
import qualified Ganeti.HTools.Node as Node
59
import qualified Ganeti.HTools.Instance as Instance
49 60
import Ganeti.HTools.Loader (mergeData, checkData, ClusterData(..)
50 61
                            , commonSuffix, clearDynU)
51 62

  
52 63
import Ganeti.BasicTypes
64
import Ganeti.Cpu.Types
65
import Ganeti.DataCollectors.Types
53 66
import Ganeti.HTools.Types
54 67
import Ganeti.HTools.CLI
68
import Ganeti.JSON
69
import Ganeti.Logging (logWarning)
55 70
import Ganeti.Utils (sepSplit, tryRead, exitIfBad, exitWhen)
56 71

  
57 72
-- | Error beautifier.
......
115 130
      ldresult = input_data >>= (if ignoreDynU then clearDynU else return)
116 131
                            >>= mergeData eff_u exTags selInsts exInsts now
117 132
  cdata <- exitIfBad "failed to load data, aborting" ldresult
118
  let (fix_msgs, nl) = checkData (cdNodes cdata) (cdInstances cdata)
133
  cdata' <- if optMonD opts then queryAllMonDDCs cdata opts else return cdata
134
  let (fix_msgs, nl) = checkData (cdNodes cdata') (cdInstances cdata')
119 135

  
120 136
  unless (optVerbose opts == 0) $ maybeShowWarnings fix_msgs
121 137

  
122
  return cdata {cdNodes = nl}
138
  return cdata' {cdNodes = nl}
123 139

  
124 140
-- | Function to save the cluster data to a file.
125 141
maybeSaveData :: Maybe FilePath -- ^ The file prefix to save to
......
134 150
  writeFile out_path adata
135 151
  hPrintf stderr "The cluster state %s has been written to file '%s'\n"
136 152
          msg out_path
153

  
154
-- | Type describing a data collector basic information.
155
data DataCollector = DataCollector
156
  { dName     :: String           -- ^ Name of the data collector
157
  , dCategory :: Maybe DCCategory -- ^ The name of the category
158
  }
159

  
160
-- | The actual data types for MonD's Data Collectors.
161
data Report = CPUavgloadReport CPUavgload
162

  
163
-- | The list of Data Collectors used by hail and hbal.
164
collectors :: Options -> [DataCollector]
165
collectors opts =
166
  if optIgnoreDynu opts
167
    then []
168
    else [ DataCollector CPUload.dcName CPUload.dcCategory ]
169

  
170
-- | Query all MonDs for all Data Collector.
171
queryAllMonDDCs :: ClusterData -> IO ClusterData
172
queryAllMonDDCs cdata = do
173
  let (ClusterData _ nl il _ _) = cdata
174
  (nl', il') <- foldM queryAllMonDs (nl, il) (collectors opts)
175
  return $ cdata {cdNodes = nl', cdInstances = il'}
176

  
177
-- | Query all MonDs for a single Data Collector.
178
queryAllMonDs :: (Node.List, Instance.List) -> DataCollector
179
                 -> IO (Node.List, Instance.List)
180
queryAllMonDs (nl, il) dc = do
181
  elems <- mapM (queryAMonD dc) (Container.elems nl)
182
  let elems' = catMaybes elems
183
  if length elems == length elems'
184
    then
185
      let il' = foldl updateUtilData il elems'
186
          nl' = zip (Container.keys nl) elems'
187
      in return (Container.fromList nl', il')
188
    else do
189
      logWarning $ "Didn't receive an answer by all MonDs, " ++ dName dc
190
                   ++ "'s data will be ignored."
191
      return (nl,il)
192

  
193
-- | Query a specified MonD for a Data Collector.
194
fromCurl :: DataCollector -> Node.Node -> IO (Maybe DCReport)
195
fromCurl dc node = do
196
  (code, !body) <-  curlGetString (prepareUrl dc node) []
197
  case code of
198
    CurlOK ->
199
      case J.decodeStrict body :: J.Result DCReport of
200
        J.Ok r -> return $ Just r
201
        J.Error _ -> return Nothing
202
    _ -> do
203
      logWarning $ "Failed to contact node's " ++ Node.name node
204
                   ++ " MonD for DC " ++ dName dc
205
      return Nothing
206

  
207
-- | Return the data from correct combination of a Data Collector
208
-- and a DCReport.
209
mkReport :: DataCollector -> Maybe DCReport -> Maybe Report
210
mkReport dc dcr =
211
  case dcr of
212
    Nothing -> Nothing
213
    Just dcr' ->
214
      case () of
215
           _ | CPUload.dcName == dName dc ->
216
                 case fromJVal (dcReportData dcr') :: Result CPUavgload of
217
                   Ok cav -> Just $ CPUavgloadReport cav
218
                   Bad _ -> Nothing
219
             | otherwise -> Nothing
220

  
221
-- | Query a MonD for a single Data Collector.
222
queryAMonD :: DataCollector -> Node.Node -> IO (Maybe Node.Node)
223
queryAMonD dc node = do
224
  dcReport <- fromCurl dc node
225
  case mkReport dc dcReport of
226
    Nothing -> return Nothing
227
    Just report ->
228
      case report of
229
        CPUavgloadReport cav ->
230
          let ct = cavCpuTotal cav
231
              du = Node.utilLoad node
232
              du' = du {cpuWeight = ct}
233
          in return $ Just node {Node.utilLoad = du'}
234

  
235
-- | Update utilization data.
236
updateUtilData :: Instance.List -> Node.Node -> Instance.List
237
updateUtilData il node =
238
  let ct = cpuWeight (Node.utilLoad node)
239
      n_uCpu = Node.uCpu node
240
      upd inst =
241
        if Node.idx node == Instance.pNode inst
242
          then
243
            let i_vcpus = Instance.vcpus inst
244
                i_util = ct / fromIntegral n_uCpu * fromIntegral i_vcpus
245
                i_du = Instance.util inst
246
                i_du' = i_du {cpuWeight = i_util}
247
            in inst {Instance.util = i_du'}
248
          else inst
249
  in Container.map upd il
250

  
251
-- | Prepare url to query a single collector.
252
prepareUrl :: DataCollector -> Node.Node -> URLString
253
prepareUrl dc node =
254
  Node.name node ++ ":" ++ show C.defaultMondPort ++ "/"
255
  ++ show C.mondLatestApiVersion ++ "/report/" ++
256
  getDCCName (dCategory dc) ++ "/" ++ dName dc
257

  
258
-- | Get Category Name.
259
getDCCName :: Maybe DCCategory -> String
260
getDCCName dcc =
261
  case dcc of
262
    Nothing -> "default"
263
    Just c -> getCategoryName c

Also available in: Unified diff