root / src / Ganeti / Confd / Client.hs @ 014eaaa0
History | View | Annotate | Download (5 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 | 199b241c | Michele Tartara | import Ganeti.Utils |
46 | 04063ba7 | Michele Tartara | |
47 | 7dc27988 | Michele Tartara | -- | Builds a properly initialized ConfdClient. |
48 | 7dc27988 | Michele Tartara | -- The parameters (an IP address and the port number for the Confd client |
49 | 7dc27988 | Michele Tartara | -- to connect to) are mainly meant for testing purposes. If they are not |
50 | 7dc27988 | Michele Tartara | -- provided, the list of master candidates and the default port number will |
51 | 7dc27988 | Michele Tartara | -- be used. |
52 | 7dc27988 | Michele Tartara | getConfdClient :: Maybe String -> Maybe Int -> IO ConfdClient |
53 | 7dc27988 | Michele Tartara | getConfdClient addr portNum = S.withSocketsDo $ do |
54 | 04063ba7 | Michele Tartara | hmac <- getClusterHmac |
55 | 04063ba7 | Michele Tartara | candList <- getMasterCandidatesIps Nothing |
56 | 04063ba7 | Michele Tartara | peerList <- |
57 | 04063ba7 | Michele Tartara | case candList of |
58 | 04063ba7 | Michele Tartara | (Ok p) -> return p |
59 | 04063ba7 | Michele Tartara | (Bad msg) -> fail msg |
60 | 7dc27988 | Michele Tartara | let addrList = maybe peerList (:[]) addr |
61 | 7dc27988 | Michele Tartara | port = fromMaybe C.defaultConfdPort portNum |
62 | 7dc27988 | Michele Tartara | return . ConfdClient hmac addrList $ fromIntegral port |
63 | 04063ba7 | Michele Tartara | |
64 | 04063ba7 | Michele Tartara | -- | Sends a query to all the Confd servers the client is connected to. |
65 | 04063ba7 | Michele Tartara | -- Returns the most up-to-date result according to the serial number, |
66 | 04063ba7 | Michele Tartara | -- chosen between those received before the timeout. |
67 | 04063ba7 | Michele Tartara | query :: ConfdClient -> ConfdRequestType -> ConfdQuery -> IO (Maybe ConfdReply) |
68 | 04063ba7 | Michele Tartara | query client crType cQuery = do |
69 | 04063ba7 | Michele Tartara | semaphore <- newMVar () |
70 | 04063ba7 | Michele Tartara | answer <- newMVar Nothing |
71 | 04063ba7 | Michele Tartara | let dest = [(host, serverPort client) | host <- peers client] |
72 | 04063ba7 | Michele Tartara | hmac = hmacKey client |
73 | 04063ba7 | Michele Tartara | jobs = map (queryOneServer semaphore answer crType cQuery hmac) dest |
74 | 04063ba7 | Michele Tartara | watchdog reqAnswers = do |
75 | 04063ba7 | Michele Tartara | threadDelay $ 1000000 * C.confdClientExpireTimeout |
76 | 04063ba7 | Michele Tartara | _ <- swapMVar reqAnswers 0 |
77 | 04063ba7 | Michele Tartara | putMVar semaphore () |
78 | 04063ba7 | Michele Tartara | waitForResult reqAnswers = do |
79 | 04063ba7 | Michele Tartara | _ <- takeMVar semaphore |
80 | 04063ba7 | Michele Tartara | l <- takeMVar reqAnswers |
81 | 04063ba7 | Michele Tartara | unless (l == 0) $ do |
82 | 04063ba7 | Michele Tartara | putMVar reqAnswers $ l - 1 |
83 | 04063ba7 | Michele Tartara | waitForResult reqAnswers |
84 | 04063ba7 | Michele Tartara | reqAnswers <- newMVar . min C.confdDefaultReqCoverage $ length dest |
85 | 04063ba7 | Michele Tartara | workers <- mapM forkIO jobs |
86 | 04063ba7 | Michele Tartara | watcher <- forkIO $ watchdog reqAnswers |
87 | 04063ba7 | Michele Tartara | waitForResult reqAnswers |
88 | 04063ba7 | Michele Tartara | mapM_ killThread $ watcher:workers |
89 | 04063ba7 | Michele Tartara | takeMVar answer |
90 | 04063ba7 | Michele Tartara | |
91 | 04063ba7 | Michele Tartara | -- | Updates the reply to the query. As per the Confd design document, |
92 | 04063ba7 | Michele Tartara | -- only the reply with the highest serial number is kept. |
93 | 04063ba7 | Michele Tartara | updateConfdReply :: ConfdReply -> Maybe ConfdReply -> Maybe ConfdReply |
94 | 04063ba7 | Michele Tartara | updateConfdReply newValue Nothing = Just newValue |
95 | 04063ba7 | Michele Tartara | updateConfdReply newValue (Just currentValue) = Just $ |
96 | 04063ba7 | Michele Tartara | if confdReplyStatus newValue == ReplyStatusOk |
97 | 2663321c | Michele Tartara | && (confdReplyStatus currentValue /= ReplyStatusOk |
98 | 2663321c | Michele Tartara | || confdReplySerial newValue > confdReplySerial currentValue) |
99 | 04063ba7 | Michele Tartara | then newValue |
100 | 04063ba7 | Michele Tartara | else currentValue |
101 | 04063ba7 | Michele Tartara | |
102 | 04063ba7 | Michele Tartara | -- | Send a query to a single server, waits for the result and stores it |
103 | 04063ba7 | Michele Tartara | -- in a shared variable. Then, sends a signal on another shared variable |
104 | 04063ba7 | Michele Tartara | -- acting as a semaphore. |
105 | 04063ba7 | Michele Tartara | -- This function is meant to be used as one of multiple threads querying |
106 | 04063ba7 | Michele Tartara | -- multiple servers in parallel. |
107 | 04063ba7 | Michele Tartara | queryOneServer |
108 | 04063ba7 | Michele Tartara | :: MVar () -- ^ The semaphore that will be signalled |
109 | 04063ba7 | Michele Tartara | -> MVar (Maybe ConfdReply) -- ^ The shared variable for the result |
110 | 04063ba7 | Michele Tartara | -> ConfdRequestType -- ^ The type of the query to be sent |
111 | 04063ba7 | Michele Tartara | -> ConfdQuery -- ^ The content of the query |
112 | 04063ba7 | Michele Tartara | -> HashKey -- ^ The hmac key to sign the message |
113 | 04063ba7 | Michele Tartara | -> (String, S.PortNumber) -- ^ The address and port of the server |
114 | 04063ba7 | Michele Tartara | -> IO () |
115 | 04063ba7 | Michele Tartara | queryOneServer semaphore answer crType cQuery hmac (host, port) = do |
116 | 04063ba7 | Michele Tartara | request <- newConfdRequest crType cQuery |
117 | 04063ba7 | Michele Tartara | timestamp <- fmap show epochTime |
118 | 04063ba7 | Michele Tartara | let signedMsg = |
119 | 04063ba7 | Michele Tartara | signMessage hmac timestamp (J.encodeStrict request) |
120 | 04063ba7 | Michele Tartara | completeMsg = C.confdMagicFourcc ++ J.encodeStrict signedMsg |
121 | 199b241c | Michele Tartara | addr <- resolveAddr (fromIntegral port) host |
122 | 199b241c | Michele Tartara | (af_family, sockaddr) <- |
123 | 199b241c | Michele Tartara | exitIfBad "Unable to resolve the IP address" addr |
124 | 199b241c | Michele Tartara | s <- S.socket af_family S.Datagram S.defaultProtocol |
125 | 199b241c | Michele Tartara | _ <- S.sendTo s completeMsg sockaddr |
126 | 04063ba7 | Michele Tartara | replyMsg <- S.recv s C.maxUdpDataSize |
127 | 04063ba7 | Michele Tartara | parsedReply <- |
128 | 04063ba7 | Michele Tartara | if C.confdMagicFourcc `isPrefixOf` replyMsg |
129 | 04063ba7 | Michele Tartara | then return . parseReply hmac (drop 4 replyMsg) $ confdRqRsalt request |
130 | 04063ba7 | Michele Tartara | else fail "Invalid magic code!" |
131 | 04063ba7 | Michele Tartara | reply <- |
132 | 04063ba7 | Michele Tartara | case parsedReply of |
133 | 04063ba7 | Michele Tartara | Ok (_, r) -> return r |
134 | 04063ba7 | Michele Tartara | Bad msg -> fail msg |
135 | 04063ba7 | Michele Tartara | modifyMVar_ answer $! return . updateConfdReply reply |
136 | 04063ba7 | Michele Tartara | putMVar semaphore () |