Add Kind data type for data collectors
[ganeti-local] / src / Ganeti / DataCollectors / Drbd.hs
1 {-| DRBD data collector.
2
3 -}
4
5 {-
6
7 Copyright (C) 2012, 2013 Google Inc.
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 02110-1301, USA.
23
24 -}
25
26 module Ganeti.DataCollectors.Drbd
27   ( main
28   , options
29   , arguments
30   , dcName
31   , dcVersion
32   , dcFormatVersion
33   , dcCategory
34   , dcKind
35   ) where
36
37
38 import qualified Control.Exception as E
39 import Control.Monad
40 import Data.Attoparsec.Text.Lazy as A
41 import Data.Maybe
42 import Data.Text.Lazy (pack, unpack)
43 import Network.BSD (getHostName)
44 import qualified Text.JSON as J
45
46 import qualified Ganeti.BasicTypes as BT
47 import qualified Ganeti.Constants as C
48 import Ganeti.Block.Drbd.Parser(drbdStatusParser)
49 import Ganeti.Block.Drbd.Types(DrbdInstMinor)
50 import Ganeti.Common
51 import Ganeti.Confd.Client
52 import Ganeti.Confd.Types
53 import Ganeti.DataCollectors.CLI
54 import Ganeti.DataCollectors.Types
55 import Ganeti.Utils
56
57
58 -- | The default path of the DRBD status file.
59 -- It is hardcoded because it is not likely to change.
60 defaultFile :: FilePath
61 defaultFile = C.drbdStatusFile
62
63 -- | The default setting for the maximum amount of not parsed character to
64 -- print in case of error.
65 -- It is set to use most of the screen estate on a standard 80x25 terminal.
66 -- TODO: add the possibility to set this with a command line parameter.
67 defaultCharNum :: Int
68 defaultCharNum = 80*20
69
70 -- | The name of this data collector.
71 dcName :: String
72 dcName = "drbd"
73
74 -- | The version of this data collector.
75 dcVersion :: DCVersion
76 dcVersion = DCVerBuiltin
77
78 -- | The version number for the data format of this data collector.
79 dcFormatVersion :: Int
80 dcFormatVersion = 1
81
82 -- | The category of this data collector.
83 dcCategory :: Maybe DCCategory
84 dcCategory = Just DCStorage
85
86 -- | The kind of this data collector.
87 dcKind :: DCKind
88 dcKind = DCKStatus
89
90 -- * Command line options
91
92 options :: IO [OptType]
93 options =
94   return
95     [ oDrbdStatus
96     , oDrbdPairing
97     ]
98
99 -- | The list of arguments supported by the program.
100 arguments :: [ArgCompletion]
101 arguments = [ArgCompletion OptComplFile 0 (Just 0)]
102
103 -- | Get information about the pairing of DRBD minors and Ganeti instances
104 -- on the current node. The information is taken from the Confd client
105 -- or, if a filename is specified, from a JSON encoded file (for testing
106 -- purposes).
107 getPairingInfo :: Maybe String -> IO (BT.Result [DrbdInstMinor])
108 getPairingInfo Nothing = do
109   curNode <- getHostName
110   client <- getConfdClient Nothing Nothing
111   reply <- query client ReqNodeDrbd $ PlainQuery curNode
112   return $
113     case fmap (J.readJSONs . confdReplyAnswer) reply of
114       Just (J.Ok instMinor) -> BT.Ok instMinor
115       Just (J.Error msg) -> BT.Bad msg
116       Nothing -> BT.Bad "No answer from the Confd server"
117 getPairingInfo (Just filename) = do
118   content <- readFile filename
119   return $
120     case J.decode content of
121       J.Ok instMinor -> BT.Ok instMinor
122       J.Error msg -> BT.Bad msg
123
124 -- | This function builds a report with the DRBD status.
125 buildDRBDReport :: FilePath -> Maybe FilePath -> IO DCReport
126 buildDRBDReport statusFile pairingFile = do
127   contents <-
128     ((E.try $ readFile statusFile) :: IO (Either IOError String)) >>=
129       exitIfBad "reading from file" . either (BT.Bad . show) BT.Ok
130   pairingResult <- getPairingInfo pairingFile
131   pairing <- exitIfBad "Can't get pairing info" pairingResult
132   jsonData <-
133     case A.parse (drbdStatusParser pairing) $ pack contents of
134       A.Fail unparsedText contexts errorMessage -> exitErr $
135         show (Prelude.take defaultCharNum $ unpack unparsedText) ++ "\n"
136           ++ show contexts ++ "\n" ++ errorMessage
137       A.Done _ drbdStatus -> return $ J.showJSON drbdStatus
138   buildReport dcName dcVersion dcFormatVersion dcCategory dcKind jsonData
139
140 -- | Main function.
141 main :: Options -> [String] -> IO ()
142 main opts args = do
143   let statusFile = fromMaybe defaultFile $ optDrbdStatus opts
144       pairingFile = optDrbdPairing opts
145   unless (null args) . exitErr $ "This program takes exactly zero" ++
146                                   " arguments, got '" ++ unwords args ++ "'"
147   report <- buildDRBDReport statusFile pairingFile
148   putStrLn $ J.encode report