Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / Confd / Client.hs @ 11e90588

History | View | Annotate | Download (4.9 kB)

1 04063ba7 Michele Tartara
{-| Implementation of the Ganeti Confd client functionality.
2 04063ba7 Michele Tartara
3 04063ba7 Michele Tartara
-}
4 04063ba7 Michele Tartara
5 04063ba7 Michele Tartara
{-
6 04063ba7 Michele Tartara
7 04063ba7 Michele Tartara
Copyright (C) 2012 Google Inc.
8 04063ba7 Michele Tartara
9 04063ba7 Michele Tartara
This program is free software; you can redistribute it and/or modify
10 04063ba7 Michele Tartara
it under the terms of the GNU General Public License as published by
11 04063ba7 Michele Tartara
the Free Software Foundation; either version 2 of the License, or
12 04063ba7 Michele Tartara
(at your option) any later version.
13 04063ba7 Michele Tartara
14 04063ba7 Michele Tartara
This program is distributed in the hope that it will be useful, but
15 04063ba7 Michele Tartara
WITHOUT ANY WARRANTY; without even the implied warranty of
16 04063ba7 Michele Tartara
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 04063ba7 Michele Tartara
General Public License for more details.
18 04063ba7 Michele Tartara
19 04063ba7 Michele Tartara
You should have received a copy of the GNU General Public License
20 04063ba7 Michele Tartara
along with this program; if not, write to the Free Software
21 04063ba7 Michele Tartara
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 04063ba7 Michele Tartara
02110-1301, USA.
23 04063ba7 Michele Tartara
24 04063ba7 Michele Tartara
-}
25 04063ba7 Michele Tartara
26 04063ba7 Michele Tartara
module Ganeti.Confd.Client
27 04063ba7 Michele Tartara
  ( getConfdClient
28 04063ba7 Michele Tartara
  , query
29 04063ba7 Michele Tartara
  ) where
30 04063ba7 Michele Tartara
31 04063ba7 Michele Tartara
import Control.Concurrent
32 04063ba7 Michele Tartara
import Control.Monad
33 04063ba7 Michele Tartara
import Data.List
34 7dc27988 Michele Tartara
import Data.Maybe
35 04063ba7 Michele Tartara
import qualified Network.Socket as S
36 04063ba7 Michele Tartara
import System.Posix.Time
37 04063ba7 Michele Tartara
import qualified Text.JSON as J
38 04063ba7 Michele Tartara
39 04063ba7 Michele Tartara
import Ganeti.BasicTypes
40 04063ba7 Michele Tartara
import Ganeti.Confd.Types
41 04063ba7 Michele Tartara
import Ganeti.Confd.Utils
42 04063ba7 Michele Tartara
import qualified Ganeti.Constants as C
43 04063ba7 Michele Tartara
import Ganeti.Hash
44 04063ba7 Michele Tartara
import Ganeti.Ssconf
45 04063ba7 Michele Tartara
46 7dc27988 Michele Tartara
-- | Builds a properly initialized ConfdClient.
47 7dc27988 Michele Tartara
-- The parameters (an IP address and the port number for the Confd client
48 7dc27988 Michele Tartara
-- to connect to) are mainly meant for testing purposes. If they are not
49 7dc27988 Michele Tartara
-- provided, the list of master candidates and the default port number will
50 7dc27988 Michele Tartara
-- be used.
51 7dc27988 Michele Tartara
getConfdClient :: Maybe String -> Maybe Int -> IO ConfdClient
52 7dc27988 Michele Tartara
getConfdClient addr portNum = S.withSocketsDo $ do
53 04063ba7 Michele Tartara
  hmac <- getClusterHmac
54 04063ba7 Michele Tartara
  candList <- getMasterCandidatesIps Nothing
55 04063ba7 Michele Tartara
  peerList <-
56 04063ba7 Michele Tartara
    case candList of
57 04063ba7 Michele Tartara
      (Ok p) -> return p
58 04063ba7 Michele Tartara
      (Bad msg) -> fail msg
59 7dc27988 Michele Tartara
  let addrList = maybe peerList (:[]) addr
60 7dc27988 Michele Tartara
      port = fromMaybe C.defaultConfdPort portNum
61 7dc27988 Michele Tartara
  return . ConfdClient hmac addrList $ fromIntegral port
62 04063ba7 Michele Tartara
63 04063ba7 Michele Tartara
-- | Sends a query to all the Confd servers the client is connected to.
64 04063ba7 Michele Tartara
-- Returns the most up-to-date result according to the serial number,
65 04063ba7 Michele Tartara
-- chosen between those received before the timeout.
66 04063ba7 Michele Tartara
query :: ConfdClient -> ConfdRequestType -> ConfdQuery -> IO (Maybe ConfdReply)
67 04063ba7 Michele Tartara
query client crType cQuery = do
68 04063ba7 Michele Tartara
  semaphore <- newMVar ()
69 04063ba7 Michele Tartara
  answer <- newMVar Nothing
70 04063ba7 Michele Tartara
  let dest = [(host, serverPort client) | host <- peers client]
71 04063ba7 Michele Tartara
      hmac = hmacKey client
72 04063ba7 Michele Tartara
      jobs = map (queryOneServer semaphore answer crType cQuery hmac) dest
73 04063ba7 Michele Tartara
      watchdog reqAnswers = do
74 04063ba7 Michele Tartara
        threadDelay $ 1000000 * C.confdClientExpireTimeout
75 04063ba7 Michele Tartara
        _ <- swapMVar reqAnswers 0
76 04063ba7 Michele Tartara
        putMVar semaphore ()
77 04063ba7 Michele Tartara
      waitForResult reqAnswers = do
78 04063ba7 Michele Tartara
        _ <- takeMVar semaphore
79 04063ba7 Michele Tartara
        l <- takeMVar reqAnswers
80 04063ba7 Michele Tartara
        unless (l == 0) $ do
81 04063ba7 Michele Tartara
          putMVar reqAnswers $ l - 1
82 04063ba7 Michele Tartara
          waitForResult reqAnswers
83 04063ba7 Michele Tartara
  reqAnswers <- newMVar . min C.confdDefaultReqCoverage $ length dest
84 04063ba7 Michele Tartara
  workers <- mapM forkIO jobs
85 04063ba7 Michele Tartara
  watcher <- forkIO $ watchdog reqAnswers
86 04063ba7 Michele Tartara
  waitForResult reqAnswers
87 04063ba7 Michele Tartara
  mapM_ killThread $ watcher:workers
88 04063ba7 Michele Tartara
  takeMVar answer
89 04063ba7 Michele Tartara
90 04063ba7 Michele Tartara
-- | Updates the reply to the query. As per the Confd design document,
91 04063ba7 Michele Tartara
-- only the reply with the highest serial number is kept.
92 04063ba7 Michele Tartara
updateConfdReply :: ConfdReply -> Maybe ConfdReply -> Maybe ConfdReply
93 04063ba7 Michele Tartara
updateConfdReply newValue Nothing = Just newValue
94 04063ba7 Michele Tartara
updateConfdReply newValue (Just currentValue) = Just $
95 04063ba7 Michele Tartara
  if confdReplyStatus newValue == ReplyStatusOk
96 2663321c Michele Tartara
      && (confdReplyStatus currentValue /= ReplyStatusOk
97 2663321c Michele Tartara
          || confdReplySerial newValue > confdReplySerial currentValue)
98 04063ba7 Michele Tartara
    then newValue
99 04063ba7 Michele Tartara
    else currentValue
100 04063ba7 Michele Tartara
101 04063ba7 Michele Tartara
-- | Send a query to a single server, waits for the result and stores it
102 04063ba7 Michele Tartara
-- in a shared variable. Then, sends a signal on another shared variable
103 04063ba7 Michele Tartara
-- acting as a semaphore.
104 04063ba7 Michele Tartara
-- This function is meant to be used as one of multiple threads querying
105 04063ba7 Michele Tartara
-- multiple servers in parallel.
106 04063ba7 Michele Tartara
queryOneServer
107 04063ba7 Michele Tartara
  :: MVar ()                 -- ^ The semaphore that will be signalled
108 04063ba7 Michele Tartara
  -> MVar (Maybe ConfdReply) -- ^ The shared variable for the result
109 04063ba7 Michele Tartara
  -> ConfdRequestType        -- ^ The type of the query to be sent
110 04063ba7 Michele Tartara
  -> ConfdQuery              -- ^ The content of the query
111 04063ba7 Michele Tartara
  -> HashKey                 -- ^ The hmac key to sign the message
112 04063ba7 Michele Tartara
  -> (String, S.PortNumber)  -- ^ The address and port of the server
113 04063ba7 Michele Tartara
  -> IO ()
114 04063ba7 Michele Tartara
queryOneServer semaphore answer crType cQuery hmac (host, port) = do
115 04063ba7 Michele Tartara
  request <- newConfdRequest crType cQuery
116 04063ba7 Michele Tartara
  timestamp <- fmap show epochTime
117 04063ba7 Michele Tartara
  let signedMsg =
118 04063ba7 Michele Tartara
        signMessage hmac timestamp (J.encodeStrict request)
119 04063ba7 Michele Tartara
      completeMsg = C.confdMagicFourcc ++ J.encodeStrict signedMsg
120 04063ba7 Michele Tartara
  s <- S.socket S.AF_INET S.Datagram S.defaultProtocol
121 04063ba7 Michele Tartara
  hostAddr <- S.inet_addr host
122 04063ba7 Michele Tartara
  _ <- S.sendTo s completeMsg $ S.SockAddrInet port hostAddr
123 04063ba7 Michele Tartara
  replyMsg <- S.recv s C.maxUdpDataSize
124 04063ba7 Michele Tartara
  parsedReply <-
125 04063ba7 Michele Tartara
    if C.confdMagicFourcc `isPrefixOf` replyMsg
126 04063ba7 Michele Tartara
      then return . parseReply hmac (drop 4 replyMsg) $ confdRqRsalt request
127 04063ba7 Michele Tartara
      else fail "Invalid magic code!"
128 04063ba7 Michele Tartara
  reply <-
129 04063ba7 Michele Tartara
    case parsedReply of
130 04063ba7 Michele Tartara
      Ok (_, r) -> return r
131 04063ba7 Michele Tartara
      Bad msg -> fail msg
132 04063ba7 Michele Tartara
  modifyMVar_ answer $! return . updateConfdReply reply
133 04063ba7 Michele Tartara
  putMVar semaphore ()