On Fri, Jul 31, 2015 at 03:07:37PM +0200, 'Klaus Aehlig' via ganeti-devel wrote:
The confd protocol heavily relies on the serial number
to filter out outdated responses. However, the current
implementation always returned 0 as serial number. Fix
this and return a serial number that is bumped with every
change that affects the answer.

Signed-off-by: Klaus Aehlig <[email protected]>
---
src/Ganeti/Confd/Server.hs | 51 ++++++++++++++++++++++++++++------------------
1 file changed, 31 insertions(+), 20 deletions(-)

diff --git a/src/Ganeti/Confd/Server.hs b/src/Ganeti/Confd/Server.hs
index f401e5e..7af7f0a 100644
--- a/src/Ganeti/Confd/Server.hs
+++ b/src/Ganeti/Confd/Server.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE TupleSections #-}
+
{-| Implementation of the Ganeti confd server functionality.

-}
@@ -71,11 +73,11 @@ import Ganeti.Utils
type CRef = IORef (Result (ConfigData, LinkIpMap))

-- | A small type alias for readability.
-type StatusAnswer = (ConfdReplyStatus, J.JSValue)
+type StatusAnswer = (ConfdReplyStatus, J.JSValue, Int)

-- | Unknown entry standard response.
queryUnknownEntry :: StatusAnswer
-queryUnknownEntry = (ReplyStatusError, J.showJSON ConfdErrorUnknownEntry)
+queryUnknownEntry = (ReplyStatusError, J.showJSON ConfdErrorUnknownEntry, 0)

{- not used yet
-- | Internal error standard response.
@@ -85,7 +87,7 @@ queryInternalError = (ReplyStatusError, J.showJSON 
ConfdErrorInternal)

-- | Argument error standard response.
queryArgumentError :: StatusAnswer
-queryArgumentError = (ReplyStatusError, J.showJSON ConfdErrorArgument)
+queryArgumentError = (ReplyStatusError, J.showJSON ConfdErrorArgument, 0)

-- | Converter from specific error to a string format.
gntErrorToResult :: ErrorResult a -> Result a
@@ -94,7 +96,7 @@ gntErrorToResult (Ok x) = Ok x

-- * Confd base functionality

--- | Computes the node role.
+-- | Computes the node role
nodeRole :: ConfigData -> String -> Result ConfdNodeRole
nodeRole cfg name = do
  cmaster <- errToResult $ QCluster.clusterMasterNodeName cfg
@@ -120,7 +122,8 @@ getNodePipByInstanceIp cfg linkipmap link instip =
    Just instname ->
      case getInstPrimaryNode cfg instname of
        Bad _ -> queryUnknownEntry -- either instance or node not found
-        Ok node -> (ReplyStatusOk, J.showJSON (nodePrimaryIp node))
+        Ok node -> (ReplyStatusOk, J.showJSON (nodePrimaryIp node),
+                    clusterSerial $ configCluster cfg)

-- | Returns a node name for a given UUID
uuidToNodeName :: ConfigData -> String -> Result String
@@ -139,11 +142,11 @@ encodeMinors cfg (node_uuid, a, b, c, d, peer_uuid) = do
-- | Builds the response to a given query.
buildResponse :: (ConfigData, LinkIpMap) -> ConfdRequest -> Result StatusAnswer
buildResponse (cfg, _) (ConfdRequest { confdRqType = ReqPing }) =
-  return (ReplyStatusOk, J.showJSON (configVersion cfg))
+  return (ReplyStatusOk, J.showJSON (configVersion cfg), 0)

buildResponse cdata req@(ConfdRequest { confdRqType = ReqClusterMaster }) =
  case confdRqQuery req of
-    EmptyQuery -> liftM ((,) ReplyStatusOk . J.showJSON) master_name
+    EmptyQuery -> liftM ((ReplyStatusOk,,serial) . J.showJSON) master_name
    PlainQuery _ -> return queryArgumentError
    DictQuery reqq -> do
      mnode <- gntErrorToResult $ getNode cfg master_uuid
@@ -153,25 +156,28 @@ buildResponse cdata req@(ConfdRequest { confdRqType = 
ReqClusterMaster }) =
                                   ReqFieldIp -> clusterMasterIp cluster
                                   ReqFieldMNodePip -> nodePrimaryIp mnode
                      ) (confdReqQFields reqq)
-      return (ReplyStatusOk, J.showJSON fvals)
+      return (ReplyStatusOk, J.showJSON fvals, serial)
    where master_uuid = clusterMasterNode cluster
          master_name = errToResult $ QCluster.clusterMasterNodeName cfg
          cluster = configCluster cfg
          cfg = fst cdata
+          serial = clusterSerial $ configCluster cfg

buildResponse cdata req@(ConfdRequest { confdRqType = ReqNodeRoleByName }) = do
  node_name <- case confdRqQuery req of
                 PlainQuery str -> return str
                 _ -> fail $ "Invalid query type " ++ show (confdRqQuery req)
  role <- nodeRole (fst cdata) node_name
-  return (ReplyStatusOk, J.showJSON role)
+  return (ReplyStatusOk, J.showJSON role,
+          clusterSerial . configCluster $ fst cdata)

buildResponse cdata (ConfdRequest { confdRqType = ReqNodePipList }) =
  -- note: we use foldlWithKey because that's present accross more
  -- versions of the library
  return (ReplyStatusOk, J.showJSON $
          M.foldlWithKey (\accu _ n -> nodePrimaryIp n:accu) []
-          (fromContainer . configNodes . fst $ cdata))
+          (fromContainer . configNodes . fst $ cdata),
+          clusterSerial . configCluster $ fst cdata)

buildResponse cdata (ConfdRequest { confdRqType = ReqMcPipList }) =
  -- note: we use foldlWithKey because that's present accross more
@@ -180,7 +186,8 @@ buildResponse cdata (ConfdRequest { confdRqType = 
ReqMcPipList }) =
          M.foldlWithKey (\accu _ n -> if nodeMasterCandidate n
                                         then nodePrimaryIp n:accu
                                         else accu) []
-          (fromContainer . configNodes . fst $ cdata))
+          (fromContainer . configNodes . fst $ cdata),
+          clusterSerial . configCluster $ fst cdata)

buildResponse (cfg, linkipmap)
              req@(ConfdRequest { confdRqType = ReqInstIpsList }) = do
@@ -188,7 +195,8 @@ buildResponse (cfg, linkipmap)
            PlainQuery str -> return str
            EmptyQuery -> return (getDefaultNicLink cfg)
            _ -> fail "Invalid query type"
-  return (ReplyStatusOk, J.showJSON $ getInstancesIpByLink linkipmap link)
+  return (ReplyStatusOk, J.showJSON $ getInstancesIpByLink linkipmap link,
+          clusterSerial $ configCluster cfg)

buildResponse cdata (ConfdRequest { confdRqType = ReqNodePipByInstPip
                                  , confdRqQuery = DictQuery query}) =
@@ -199,7 +207,8 @@ buildResponse cdata (ConfdRequest { confdRqType = 
ReqNodePipByInstPip
       Nothing -> return (ReplyStatusOk,
                          J.showJSON $
                           map (getNodePipByInstanceIp cfg linkipmap link)
-                           (confdReqQIpList query))
+                           (confdReqQIpList query),
+                          clusterSerial . configCluster $ fst cdata)

buildResponse _ (ConfdRequest { confdRqType = ReqNodePipByInstPip }) =
  return queryArgumentError
@@ -213,7 +222,7 @@ buildResponse cdata req@(ConfdRequest { confdRqType = 
ReqNodeDrbd }) = do
  let minors = concatMap (getInstMinorsForNode cfg (nodeUuid node)) .
               M.elems . fromContainer . configInstances $ cfg
  encoded <- mapM (encodeMinors cfg) minors
-  return (ReplyStatusOk, J.showJSON encoded)
+  return (ReplyStatusOk, J.showJSON encoded, nodeSerial node)

-- | Return the list of instances for a node (as ([primary], [secondary])) given
-- the node name.
@@ -228,7 +237,7 @@ buildResponse cdata req@(ConfdRequest { confdRqType = 
ReqNodeInstances }) = do
      Bad e -> fail $ "Node not found in the configuration: " ++ show e
  let node_uuid = nodeUuid node
      instances = getNodeInstances cfg node_uuid
-  return (ReplyStatusOk, J.showJSON instances)
+  return (ReplyStatusOk, J.showJSON instances, nodeSerial node)

-- | Return the list of disks for an instance given the instance uuid.
buildResponse cdata req@(ConfdRequest { confdRqType = ReqInstanceDisks }) = do
@@ -237,20 +246,22 @@ buildResponse cdata req@(ConfdRequest { confdRqType = 
ReqInstanceDisks }) = do
    case confdRqQuery req of
      PlainQuery str -> return str
      _ -> fail $ "Invalid query type " ++ show (confdRqQuery req)
+  inst <- lookupContainer (Bad $ "unknown instance: " ++ inst_uuid)
+            inst_uuid $ configInstances cfg
  case getInstDisks cfg inst_uuid of
-    Ok disks -> return (ReplyStatusOk, J.showJSON disks)
+    Ok disks -> return (ReplyStatusOk, J.showJSON disks, instSerial inst)
    Bad e -> fail $ "Could not retrieve disks: " ++ show e

-- | Creates a ConfdReply from a given answer.
serializeResponse :: Result StatusAnswer -> ConfdReply
serializeResponse r =
-    let (status, result) = case r of
-                    Bad err -> (ReplyStatusError, J.showJSON err)
-                    Ok (code, val) -> (code, val)
+    let (status, result, serial) = case r of
+                    Bad err -> (ReplyStatusError, J.showJSON err, 0)
+                    Ok (code, val, ser) -> (code, val, ser)
    in ConfdReply { confdReplyProtocol = 1
                  , confdReplyStatus   = status
                  , confdReplyAnswer   = result
-                  , confdReplySerial   = 0 }
+                  , confdReplySerial   = serial }

-- ** Client input/output handlers

--
2.5.0.rc2.392.g76e840b


LGTM, thanks

Reply via email to