Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / DataCollectors / Lv.hs @ 820ca72d

History | View | Annotate | Download (6 kB)

1
{-| Logical Volumes data collector.
2

    
3
-}
4

    
5
{-
6

    
7
Copyright (C) 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.Lv
27
  ( main
28
  , options
29
  , arguments
30
  , dcName
31
  , dcVersion
32
  , dcFormatVersion
33
  , dcCategory
34
  , dcKind
35
  , dcReport
36
  ) where
37

    
38

    
39
import qualified Control.Exception as E
40
import Control.Monad
41
import Data.Attoparsec.Text.Lazy as A
42
import Data.List
43
import Data.Text.Lazy (pack, unpack)
44
import Network.BSD (getHostName)
45
import System.Process
46
import qualified Text.JSON as J
47

    
48
import qualified Ganeti.BasicTypes as BT
49
import Ganeti.Common
50
import Ganeti.Confd.ClientFunctions
51
import Ganeti.DataCollectors.CLI
52
import Ganeti.DataCollectors.Types
53
import Ganeti.JSON
54
import Ganeti.Objects
55
import Ganeti.Storage.Lvm.LVParser
56
import Ganeti.Storage.Lvm.Types
57
import Ganeti.Utils
58

    
59

    
60
-- | The default setting for the maximum amount of not parsed character to
61
-- print in case of error.
62
-- It is set to use most of the screen estate on a standard 80x25 terminal.
63
-- TODO: add the possibility to set this with a command line parameter.
64
defaultCharNum :: Int
65
defaultCharNum = 80*20
66

    
67
-- | The name of this data collector.
68
dcName :: String
69
dcName = "lv"
70

    
71
-- | The version of this data collector.
72
dcVersion :: DCVersion
73
dcVersion = DCVerBuiltin
74

    
75
-- | The version number for the data format of this data collector.
76
dcFormatVersion :: Int
77
dcFormatVersion = 1
78

    
79
-- | The category of this data collector.
80
dcCategory :: Maybe DCCategory
81
dcCategory = Just DCStorage
82

    
83
-- | The kind of this data collector.
84
dcKind :: DCKind
85
dcKind = DCKPerf
86

    
87
-- | The data exported by the data collector, taken from the default location.
88
dcReport :: IO DCReport
89
dcReport = buildDCReport defaultOptions
90

    
91
-- * Command line options
92

    
93
options :: IO [OptType]
94
options =
95
  return
96
    [ oInputFile
97
    , oConfdAddr
98
    , oConfdPort
99
    , oInstances
100
    ]
101

    
102
-- | The list of arguments supported by the program.
103
arguments :: [ArgCompletion]
104
arguments = [ArgCompletion OptComplFile 0 (Just 0)]
105

    
106
-- | Get information about logical volumes from file (if specified) or
107
-- by actually running the command to get it from a live cluster.
108
getLvInfo :: Maybe FilePath -> IO [LVInfo]
109
getLvInfo inputFile = do
110
  let cmd = lvCommand
111
      params = lvParams
112
      fromLvs =
113
        ((E.try $ readProcess cmd params "") :: IO (Either IOError String)) >>=
114
        exitIfBad "running command" . either (BT.Bad . show) BT.Ok
115
  contents <-
116
    maybe fromLvs (\fn -> ((E.try $ readFile fn) :: IO (Either IOError String))
117
      >>= exitIfBad "reading from file" . either (BT.Bad . show) BT.Ok)
118
      inputFile
119
  case A.parse lvParser $ pack contents of
120
    A.Fail unparsedText contexts errorMessage -> exitErr $
121
      show (Prelude.take defaultCharNum $ unpack unparsedText) ++ "\n"
122
        ++ show contexts ++ "\n" ++ errorMessage
123
    A.Done _ lvinfoD -> return lvinfoD
124

    
125
-- | Get the list of instances on the current node (both primary and secondary)
126
-- either from a provided file or by querying Confd.
127
getInstanceList :: Options -> IO ([Instance], [Instance])
128
getInstanceList opts = do
129
  let srvAddr = optConfdAddr opts
130
      srvPort = optConfdPort opts
131
      instFile = optInstances opts
132
      fromConfdUnchecked :: IO (BT.Result ([Instance], [Instance]))
133
      fromConfdUnchecked = getHostName >>= \n -> getInstances n srvAddr srvPort
134
      fromConfd :: IO (BT.Result ([Instance], [Instance]))
135
      fromConfd =
136
        liftM (either (BT.Bad . show) id) (E.try fromConfdUnchecked :: 
137
          IO (Either IOError (BT.Result ([Instance], [Instance]))))
138
      fromFile :: FilePath -> IO (BT.Result ([Instance], [Instance]))
139
      fromFile inputFile = do
140
        contents <-
141
          ((E.try $ readFile inputFile) :: IO (Either IOError String))
142
            >>= exitIfBad "reading from file" . either (BT.Bad . show) BT.Ok
143
        return . fromJResult "Not a list of instances" $ J.decode contents
144
  instances <- maybe fromConfd fromFile instFile
145
  exitIfBad "Unable to obtain the list of instances" instances
146

    
147
-- | Adds the name of the instance to the information about one logical volume.
148
addInstNameToOneLv :: [Instance] -> LVInfo -> LVInfo
149
addInstNameToOneLv instances lvInfo =
150
  let vg_name = lviVgName lvInfo
151
      lv_name = lviName lvInfo
152
      instanceHasDisk = any (includesLogicalId vg_name lv_name) . instDisks
153
      rightInstance = find instanceHasDisk instances
154
    in 
155
      case rightInstance of
156
        Nothing -> lvInfo
157
        Just i -> lvInfo { lviInstance = Just $ instName i }
158

    
159
-- | Adds the name of the instance to the information about logical volumes.
160
addInstNameToLv :: [Instance] -> [LVInfo] -> [LVInfo]
161
addInstNameToLv instances = map (addInstNameToOneLv instances)
162

    
163
-- | This function computes the JSON representation of the LV status.
164
buildJsonReport :: Options -> IO J.JSValue
165
buildJsonReport opts = do
166
  let inputFile = optInputFile opts
167
  lvInfo <- getLvInfo inputFile
168
  (prim, sec) <- getInstanceList opts
169
  return . J.showJSON $ addInstNameToLv (prim ++ sec) lvInfo
170

    
171
-- | This function computes the DCReport for the logical volumes.
172
buildDCReport :: Options -> IO DCReport
173
buildDCReport opts =
174
  buildJsonReport opts >>=
175
    buildReport dcName dcVersion dcFormatVersion dcCategory dcKind
176

    
177
-- | Main function.
178
main :: Options -> [String] -> IO ()
179
main opts args = do
180
  unless (null args) . exitErr $ "This program takes exactly zero" ++
181
                                 " arguments, got '" ++ unwords args ++ "'"
182

    
183
  report <- buildDCReport opts
184
  putStrLn $ J.encode report