Currently, fromObj/maybeFromObj take first the key, and then the
object. This is suboptimal, as this form is not easy to use with
partial functional application.

To make it easier to switch between tryFromObj, fromObj and
maybeFromObj, we unify the latter two functions to the same order
(object and then key) like the first one. The code churn in the other
modules is due to this, but the main change in this patch is in
Utils.hs.

Furthermore, since we change anyway the calls, we do replace fromObj
with tryFromObj in IAllocator.hs so that we get better error messages.
---
 Ganeti/HTools/IAlloc.hs |   66 +++++++++++++++++++++++++----------------------
 Ganeti/HTools/Utils.hs  |   14 +++++-----
 Ganeti/Luxi.hs          |    8 +++---
 Ganeti/OpCodes.hs       |   31 +++++++++++----------
 4 files changed, 62 insertions(+), 57 deletions(-)

diff --git a/Ganeti/HTools/IAlloc.hs b/Ganeti/HTools/IAlloc.hs
index 903801a..228b408 100644
--- a/Ganeti/HTools/IAlloc.hs
+++ b/Ganeti/HTools/IAlloc.hs
@@ -4,7 +4,7 @@
 
 {-
 
-Copyright (C) 2009, 2010 Google Inc.
+Copyright (C) 2009, 2010, 2011 Google Inc.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -50,10 +50,11 @@ parseBaseInstance :: String
                   -> [(String, JSValue)]
                   -> Result (String, Instance.Instance)
 parseBaseInstance n a = do
-  disk <- fromObj "disk_space_total" a
-  mem <- fromObj "memory" a
-  vcpus <- fromObj "vcpus" a
-  tags <- fromObj "tags" a
+  let extract x = tryFromObj ("invalid data for instance '" ++ n ++ "'") a x
+  disk  <- extract "disk_space_total"
+  mem   <- extract "memory"
+  vcpus <- extract "vcpus"
+  tags  <- extract "tags"
   let running = "running"
   return (n, Instance.create n mem disk vcpus running tags 0 0)
 
@@ -64,7 +65,7 @@ parseInstance :: NameAssoc        -- ^ The node name-to-index 
association list
               -> Result (String, Instance.Instance)
 parseInstance ktn n a = do
   base <- parseBaseInstance n a
-  nodes <- fromObj "nodes" a
+  nodes <- fromObj a "nodes"
   pnode <- if null nodes
            then Bad $ "empty node list for instance " ++ n
            else readEitherString $ head nodes
@@ -80,19 +81,20 @@ parseNode :: NameAssoc           -- ^ The group association
           -> [(String, JSValue)] -- ^ The JSON object
           -> Result (String, Node.Node)
 parseNode ktg n a = do
-  offline <- fromObj "offline" a
-  drained <- fromObj "drained" a
-  guuid   <- fromObj "group" a
+  let extract x = tryFromObj ("invalid data for node '" ++ n ++ "'") a x
+  offline <- extract "offline"
+  drained <- extract "drained"
+  guuid   <- extract "group"
   gidx <- lookupGroup ktg n guuid
   node <- (if offline || drained
            then return $ Node.create n 0 0 0 0 0 0 True gidx
            else do
-             mtotal <- fromObj "total_memory" a
-             mnode  <- fromObj "reserved_memory" a
-             mfree  <- fromObj "free_memory"  a
-             dtotal <- fromObj "total_disk"   a
-             dfree  <- fromObj "free_disk"    a
-             ctotal <- fromObj "total_cpus"   a
+             mtotal <- extract "total_memory"
+             mnode  <- extract "reserved_memory"
+             mfree  <- extract "free_memory"
+             dtotal <- extract "total_disk"
+             dfree  <- extract "free_disk"
+             ctotal <- extract "total_cpus"
              return $ Node.create n mtotal mnode mfree
                     dtotal dfree ctotal False gidx)
   return (n, node)
@@ -102,7 +104,7 @@ parseGroup :: String              -- ^ The group UUID
            -> [(String, JSValue)] -- ^ The JSON object
            -> Result (String, Group.Group)
 parseGroup u a = do
-  name <- fromObj "name" a
+  name <- fromObj a "name"
   return (u, Group.create name u AllocPreferred)
 
 -- | Top-level parser.
@@ -111,48 +113,50 @@ parseData :: String         -- ^ The JSON message as 
received from Ganeti
 parseData body = do
   decoded <- fromJResult "Parsing input IAllocator message" (decodeStrict body)
   let obj = fromJSObject decoded
+      extrObj x = tryFromObj "invalid iallocator message" obj x
   -- request parser
-  request <- liftM fromJSObject (fromObj "request" obj)
+  request <- liftM fromJSObject (extrObj "request")
+  let extrReq x = tryFromObj "invalid request dict" request x
   -- existing group parsing
-  glist <- liftM fromJSObject (fromObj "nodegroups" obj)
+  glist <- liftM fromJSObject (extrObj "nodegroups")
   gobj <- mapM (\(x, y) -> asJSObject y >>= parseGroup x . fromJSObject) glist
   let (ktg, gl) = assignIndices gobj
   -- existing node parsing
-  nlist <- liftM fromJSObject (fromObj "nodes" obj)
+  nlist <- liftM fromJSObject (extrObj "nodes")
   nobj <- mapM (\(x,y) ->
                     asJSObject y >>= parseNode ktg x . fromJSObject) nlist
   let (ktn, nl) = assignIndices nobj
   -- existing instance parsing
-  ilist <- fromObj "instances" obj
+  ilist <- extrObj "instances"
   let idata = fromJSObject ilist
   iobj <- mapM (\(x,y) ->
                     asJSObject y >>= parseInstance ktn x . fromJSObject) idata
   let (kti, il) = assignIndices iobj
   -- cluster tags
-  ctags <- fromObj "cluster_tags" obj
+  ctags <- extrObj "cluster_tags"
   cdata <- mergeData [] [] [] (ClusterData gl nl il ctags)
   let map_n = cdNodes cdata
-  optype <- fromObj "type" request
+  optype <- extrReq "type"
   rqtype <-
       case optype of
         "allocate" ->
             do
-              rname <- fromObj "name" request
-              req_nodes <- fromObj "required_nodes" request
-              inew <- parseBaseInstance rname request
+              rname     <- extrReq "name"
+              req_nodes <- extrReq "required_nodes"
+              inew      <- parseBaseInstance rname request
               let io = snd inew
               return $ Allocate io req_nodes
         "relocate" ->
             do
-              rname <- fromObj "name" request
-              ridx <- lookupInstance kti rname
-              req_nodes <- fromObj "required_nodes" request
-              ex_nodes <- fromObj "relocate_from" request
-              ex_idex <- mapM (Container.findByName map_n) ex_nodes
+              rname     <- extrReq "name"
+              ridx      <- lookupInstance kti rname
+              req_nodes <- extrReq "required_nodes"
+              ex_nodes  <- extrReq "relocate_from"
+              ex_idex   <- mapM (Container.findByName map_n) ex_nodes
               return $ Relocate ridx req_nodes (map Node.idx ex_idex)
         "multi-evacuate" ->
             do
-              ex_names <- fromObj "evac_nodes" request
+              ex_names <- extrReq "evac_nodes"
               ex_nodes <- mapM (Container.findByName map_n) ex_names
               let ex_ndx = map Node.idx ex_nodes
               return $ Evacuate ex_ndx
diff --git a/Ganeti/HTools/Utils.hs b/Ganeti/HTools/Utils.hs
index 6091858..1e58bc3 100644
--- a/Ganeti/HTools/Utils.hs
+++ b/Ganeti/HTools/Utils.hs
@@ -2,7 +2,7 @@
 
 {-
 
-Copyright (C) 2009, 2010 Google Inc.
+Copyright (C) 2009, 2010, 2011 Google Inc.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -127,16 +127,16 @@ loadJSArray :: (Monad m)
 loadJSArray s = fromJResult s . J.decodeStrict
 
 -- | Reads the value of a key in a JSON object.
-fromObj :: (J.JSON a, Monad m) => String -> [(String, J.JSValue)] -> m a
-fromObj k o =
+fromObj :: (J.JSON a, Monad m) => [(String, J.JSValue)] -> String -> m a
+fromObj o k =
     case lookup k o of
       Nothing -> fail $ printf "key '%s' not found in %s" k (show o)
       Just val -> fromKeyValue k val
 
 -- | Reads the value of an optional key in a JSON object.
-maybeFromObj :: (J.JSON a, Monad m) => String -> [(String, J.JSValue)]
-                -> m (Maybe a)
-maybeFromObj k o =
+maybeFromObj :: (J.JSON a, Monad m) =>
+             [(String, J.JSValue)] -> String -> m (Maybe a)
+maybeFromObj o k =
     case lookup k o of
       Nothing -> return Nothing
       Just val -> liftM Just (fromKeyValue k val)
@@ -161,7 +161,7 @@ tryFromObj :: (J.JSON a) =>
            -> [(String, J.JSValue)] -- ^ The object array
            -> String                -- ^ The desired key from the object
            -> Result a
-tryFromObj t o k = annotateResult t (fromObj k o)
+tryFromObj t o = annotateResult t . fromObj o
 
 -- | Small wrapper over readJSON.
 fromJVal :: (Monad m, J.JSON a) => J.JSValue -> m a
diff --git a/Ganeti/Luxi.hs b/Ganeti/Luxi.hs
index 1f65f1e..56024a5 100644
--- a/Ganeti/Luxi.hs
+++ b/Ganeti/Luxi.hs
@@ -4,7 +4,7 @@
 
 {-
 
-Copyright (C) 2009, 2010 Google Inc.
+Copyright (C) 2009, 2010, 2011 Google Inc.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -201,11 +201,11 @@ validateResult s = do
   oarr <- fromJResult "Parsing LUXI response"
           (decodeStrict s)::Result (JSObject JSValue)
   let arr = J.fromJSObject oarr
-  status <- fromObj (strOfKey Success) arr::Result Bool
+  status <- fromObj arr (strOfKey Success)::Result Bool
   let rkey = strOfKey Result
   (if status
-   then fromObj rkey arr
-   else fromObj rkey arr >>= fail)
+   then fromObj arr rkey
+   else fromObj arr rkey >>= fail)
 
 -- | Generic luxi method call.
 callMethod :: LuxiOp -> Client -> IO (Result JSValue)
diff --git a/Ganeti/OpCodes.hs b/Ganeti/OpCodes.hs
index 7b3e434..972a2dc 100644
--- a/Ganeti/OpCodes.hs
+++ b/Ganeti/OpCodes.hs
@@ -4,7 +4,7 @@
 
 {-
 
-Copyright (C) 2009, 2010 Google Inc.
+Copyright (C) 2009, 2010, 2011 Google Inc.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -72,28 +72,29 @@ opID (OpMigrateInstance _ _ _) = "OP_INSTANCE_MIGRATE"
 loadOpCode :: JSValue -> J.Result OpCode
 loadOpCode v = do
   o <- liftM J.fromJSObject (readJSON v)
-  op_id <- fromObj "OP_ID" o
+  let extract x = fromObj o x
+  op_id <- extract "OP_ID"
   case op_id of
     "OP_TEST_DELAY" -> do
-                 on_nodes <- fromObj "on_nodes" o
-                 on_master <- fromObj "on_master" o
-                 duration <- fromObj "duration" o
+                 on_nodes  <- extract "on_nodes"
+                 on_master <- extract "on_master"
+                 duration  <- extract "duration"
                  return $ OpTestDelay duration on_master on_nodes
     "OP_INSTANCE_REPLACE_DISKS" -> do
-                 inst <- fromObj "instance_name" o
-                 node <- maybeFromObj "remote_node" o
-                 mode <- fromObj "mode" o
-                 disks <- fromObj "disks" o
-                 ialloc <- maybeFromObj "iallocator" o
+                 inst   <- extract "instance_name"
+                 node   <- maybeFromObj o "remote_node"
+                 mode   <- extract "mode"
+                 disks  <- extract "disks"
+                 ialloc <- maybeFromObj o "iallocator"
                  return $ OpReplaceDisks inst node mode disks ialloc
     "OP_INSTANCE_FAILOVER" -> do
-                 inst <- fromObj "instance_name" o
-                 consist <- fromObj "ignore_consistency" o
+                 inst    <- extract "instance_name"
+                 consist <- extract "ignore_consistency"
                  return $ OpFailoverInstance inst consist
     "OP_INSTANCE_MIGRATE" -> do
-                 inst <- fromObj "instance_name" o
-                 live <- fromObj "live" o
-                 cleanup <- fromObj "cleanup" o
+                 inst    <- extract "instance_name"
+                 live    <- extract "live"
+                 cleanup <- extract "cleanup"
                  return $ OpMigrateInstance inst live cleanup
     _ -> J.Error $ "Unknown opcode " ++ op_id
 
-- 
1.7.3.1

Reply via email to