module Ganeti.DataCollectors.Lv
( main
, options
, arguments
, dcName
, dcVersion
, dcFormatVersion
, dcCategory
, dcKind
, dcReport
) where
import qualified Control.Exception as E
import Control.Monad
import Data.Attoparsec.Text.Lazy as A
import Data.List
import Data.Maybe (mapMaybe)
import Data.Text.Lazy (pack, unpack)
import Network.BSD (getHostName)
import System.Process
import qualified Text.JSON as J
import qualified Ganeti.BasicTypes as BT
import Ganeti.Common
import qualified Ganeti.Constants as C
import Ganeti.Confd.ClientFunctions
import Ganeti.DataCollectors.CLI
import Ganeti.DataCollectors.Types
import Ganeti.JSON (fromJResult)
import Ganeti.Objects
import Ganeti.Storage.Lvm.LVParser
import Ganeti.Storage.Lvm.Types
import Ganeti.Utils
defaultCharNum :: Int
defaultCharNum = 80*20
dcName :: String
dcName = C.dataCollectorLv
dcVersion :: DCVersion
dcVersion = DCVerBuiltin
dcFormatVersion :: Int
dcFormatVersion = 1
dcCategory :: Maybe DCCategory
dcCategory = Just DCStorage
dcKind :: DCKind
dcKind = DCKPerf
dcReport :: IO DCReport
dcReport = buildDCReport defaultOptions
options :: IO [OptType]
options =
return
[ oInputFile
, oConfdAddr
, oConfdPort
, oInstances
]
arguments :: [ArgCompletion]
arguments = [ArgCompletion OptComplFile 0 (Just 0)]
getLvInfo :: Maybe FilePath -> IO [LVInfo]
getLvInfo inputFile = do
let cmd = lvCommand
params = lvParams
fromLvs =
((E.try $ readProcess cmd params "") :: IO (Either IOError String)) >>=
exitIfBad "running command" . either (BT.Bad . show) BT.Ok
contents <-
maybe fromLvs (\fn -> ((E.try $ readFile fn) :: IO (Either IOError String))
>>= exitIfBad "reading from file" . either (BT.Bad . show) BT.Ok)
inputFile
case A.parse lvParser $ pack contents of
A.Fail unparsedText contexts errorMessage -> exitErr $
show (Prelude.take defaultCharNum $ unpack unparsedText) ++ "\n"
++ show contexts ++ "\n" ++ errorMessage
A.Done _ lvinfoD -> return lvinfoD
getInstDiskList :: Options -> IO [(RealInstanceData, [Disk])]
getInstDiskList opts = do
instances <- maybe fromConfd fromFile $ optInstances opts
exitIfBad "Unable to obtain the list of instances" instances
where
fromConfdUnchecked :: IO (BT.Result [(RealInstanceData, [Disk])])
fromConfdUnchecked = do
let srvAddr = optConfdAddr opts
srvPort = optConfdPort opts
toReal (RealInstance i, dsks) = Just (i, dsks)
toReal _ = Nothing
getHostName >>= \n -> BT.runResultT
. liftM (mapMaybe toReal)
$ getInstanceDisks n srvAddr srvPort
fromConfd :: IO (BT.Result [(RealInstanceData, [Disk])])
fromConfd =
liftM (either (BT.Bad . show) id)
(E.try fromConfdUnchecked ::
IO (Either IOError (BT.Result [(RealInstanceData, [Disk])])))
fromFile :: FilePath -> IO (BT.Result [(RealInstanceData, [Disk])])
fromFile inputFile = do
contents <-
((E.try $ readFile inputFile) :: IO (Either IOError String))
>>= exitIfBad "reading from file" . either (BT.Bad . show) BT.Ok
return . fromJResult "Not a list of instances" $ J.decode contents
addInstNameToOneLv :: [(RealInstanceData, [Disk])] -> LVInfo -> LVInfo
addInstNameToOneLv instDiskList lvInfo =
let lv = LogicalVolume (lviVgName lvInfo) (lviName lvInfo)
instanceHasDisk = any (includesLogicalId lv) . snd
rightInstance = find instanceHasDisk instDiskList
in
case rightInstance of
Nothing -> lvInfo
Just (i, _) -> lvInfo { lviInstance = Just $ realInstName i }
addInstNameToLv :: [(RealInstanceData, [Disk])] -> [LVInfo] -> [LVInfo]
addInstNameToLv instDisksList = map (addInstNameToOneLv instDisksList)
buildJsonReport :: Options -> IO J.JSValue
buildJsonReport opts = do
let inputFile = optInputFile opts
lvInfo <- getLvInfo inputFile
instDiskList <- getInstDiskList opts
return . J.showJSON $ addInstNameToLv instDiskList lvInfo
buildDCReport :: Options -> IO DCReport
buildDCReport opts =
buildJsonReport opts >>=
buildReport dcName dcVersion dcFormatVersion dcCategory dcKind
main :: Options -> [String] -> IO ()
main opts args = do
unless (null args) . exitErr $ "This program takes exactly zero" ++
" arguments, got '" ++ unwords args ++ "'"
report <- buildDCReport opts
putStrLn $ J.encode report