Statistics
| Branch: | Tag: | Revision:

root / Ganeti / HTools / IAlloc.hs @ 9188aeef

History | View | Annotate | Download (5.2 kB)

1
{-| Implementation of the iallocator interface.
2

    
3
-}
4

    
5
module Ganeti.HTools.IAlloc
6
    (
7
      parseData
8
    , formatResponse
9
    , RqType(..)
10
    , Request(..)
11
    ) where
12

    
13
import Data.Either ()
14
import Control.Monad
15
import Text.JSON (JSObject, JSValue(JSBool, JSString, JSArray),
16
                  makeObj, encodeStrict, decodeStrict,
17
                  fromJSObject, toJSString)
18
import qualified Ganeti.HTools.Container as Container
19
import qualified Ganeti.HTools.Node as Node
20
import qualified Ganeti.HTools.Instance as Instance
21
import Ganeti.HTools.Loader
22
import Ganeti.HTools.Utils
23
import Ganeti.HTools.Types
24

    
25
-- | The request type.
26
data RqType
27
    = Allocate Instance.Instance Int -- ^ A new instance allocation
28
    | Relocate Idx Int [Ndx]         -- ^ Move an instance to a new
29
                                     -- secondary node
30
    deriving (Show)
31

    
32
-- | A complete request, as received from Ganeti.
33
data Request = Request RqType Node.List Instance.List String
34
    deriving (Show)
35

    
36
-- | Parse the basic specifications of an instance.
37
--
38
-- Instances in the cluster instance list and the instance in an
39
-- 'Allocate' request share some common properties, which are read by
40
-- this function.
41
parseBaseInstance :: String
42
                  -> JSObject JSValue
43
                  -> Result (String, Instance.Instance)
44
parseBaseInstance n a = do
45
  disk <- case fromObj "disk_usage" a of
46
            Bad _ -> do
47
                all_d <- fromObj "disks" a >>= asObjectList
48
                szd <- mapM (fromObj "size") all_d
49
                let sze = map (+128) szd
50
                    szf = (sum sze)::Int
51
                return szf
52
            x@(Ok _) -> x
53
  mem <- fromObj "memory" a
54
  let running = "running"
55
  return $ (n, Instance.create n mem disk running 0 0)
56

    
57
-- | Parses an instance as found in the cluster instance list.
58
parseInstance :: NameAssoc        -- ^ The node name-to-index association list
59
              -> String           -- ^ The name of the instance
60
              -> JSObject JSValue -- ^ The JSON object
61
              -> Result (String, Instance.Instance)
62
parseInstance ktn n a = do
63
    base <- parseBaseInstance n a
64
    nodes <- fromObj "nodes" a
65
    pnode <- readEitherString $ head nodes
66
    pidx <- lookupNode ktn n pnode
67
    let snodes = tail nodes
68
    sidx <- (if null snodes then return Node.noSecondary
69
             else (readEitherString $ head snodes) >>= lookupNode ktn n)
70
    return (n, Instance.setBoth (snd base) pidx sidx)
71

    
72
-- | Parses a node as found in the cluster node list.
73
parseNode :: String           -- ^ The node's name
74
          -> JSObject JSValue -- ^ The JSON object
75
          -> Result (String, Node.Node)
76
parseNode n a = do
77
    let name = n
78
    offline <- fromObj "offline" a
79
    drained <- fromObj "drained" a
80
    node <- (case offline of
81
               True -> return $ Node.create name 0 0 0 0 0 True
82
               _ -> do
83
                 mtotal <- fromObj "total_memory" a
84
                 mnode <- fromObj "reserved_memory" a
85
                 mfree <- fromObj "free_memory" a
86
                 dtotal <- fromObj "total_disk" a
87
                 dfree <- fromObj "free_disk" a
88
                 return $ Node.create n mtotal mnode mfree
89
                        dtotal dfree (offline || drained))
90
    return (name, node)
91

    
92
-- | Top-level parser.
93
parseData :: String         -- ^ The JSON message as received from Ganeti
94
          -> Result Request -- ^ A (possible valid) request
95
parseData body = do
96
  decoded <- fromJResult $ decodeStrict body
97
  let obj = decoded
98
  -- request parser
99
  request <- fromObj "request" obj
100
  rname <- fromObj "name" request
101
  -- existing node parsing
102
  nlist <- fromObj "nodes" obj
103
  let ndata = fromJSObject nlist
104
  nobj <- (mapM (\(x,y) -> asJSObject y >>= parseNode x)) ndata
105
  let (ktn, nl) = assignIndices nobj
106
  -- existing instance parsing
107
  ilist <- fromObj "instances" obj
108
  let idata = fromJSObject ilist
109
  iobj <- (mapM (\(x,y) -> asJSObject y >>= parseInstance ktn x)) idata
110
  let (kti, il) = assignIndices iobj
111
  (map_n, map_i, csf) <- mergeData (nl, il)
112
  req_nodes <- fromObj "required_nodes" request
113
  optype <- fromObj "type" request
114
  rqtype <-
115
      case optype of
116
        "allocate" ->
117
            do
118
              inew <- parseBaseInstance rname request
119
              let io = snd inew
120
              return $ Allocate io req_nodes
121
        "relocate" ->
122
            do
123
              ridx <- lookupInstance kti rname
124
              ex_nodes <- fromObj "relocate_from" request
125
              let ex_nodes' = map (stripSuffix $ length csf) ex_nodes
126
              ex_idex <- mapM (Container.findByName map_n) ex_nodes'
127
              return $ Relocate ridx req_nodes ex_idex
128
        other -> fail $ ("Invalid request type '" ++ other ++ "'")
129
  return $ Request rqtype map_n map_i csf
130

    
131
-- | Formats the response into a valid IAllocator response message.
132
formatResponse :: Bool     -- ^ Whether the request was successful
133
               -> String   -- ^ Information text
134
               -> [String] -- ^ The list of chosen nodes
135
               -> String   -- ^ The JSON-formatted message
136
formatResponse success info nodes =
137
    let
138
        e_success = ("success", JSBool success)
139
        e_info = ("info", JSString . toJSString $ info)
140
        e_nodes = ("nodes", JSArray $ map (JSString . toJSString) nodes)
141
    in encodeStrict $ makeObj [e_success, e_info, e_nodes]