module Ganeti.HTools.Text
(
loadData
, parseData
, loadInst
, loadNode
, serializeInstances
, serializeNode
, serializeNodes
, serializeCluster
) where
import Control.Monad
import Data.List
import Text.Printf (printf)
import Ganeti.HTools.Utils
import Ganeti.HTools.Loader
import Ganeti.HTools.Types
import qualified Ganeti.HTools.Container as Container
import qualified Ganeti.HTools.Group as Group
import qualified Ganeti.HTools.Node as Node
import qualified Ganeti.HTools.Instance as Instance
serializeGroup :: Group.Group -> String
serializeGroup grp =
printf "%s|%s|%s" (Group.name grp) (Group.uuid grp)
(apolToString (Group.allocPolicy grp))
serializeGroups :: Group.List -> String
serializeGroups = unlines . map serializeGroup . Container.elems
serializeNode :: Group.List
-> Node.Node
-> String
serializeNode gl node =
printf "%s|%.0f|%d|%d|%.0f|%d|%.0f|%c|%s" (Node.name node)
(Node.tMem node) (Node.nMem node) (Node.fMem node)
(Node.tDsk node) (Node.fDsk node) (Node.tCpu node)
(if Node.offline node then 'Y' else 'N')
(Group.uuid grp)
where grp = Container.find (Node.group node) gl
serializeNodes :: Group.List -> Node.List -> String
serializeNodes gl = unlines . map (serializeNode gl) . Container.elems
serializeInstance :: Node.List
-> Instance.Instance
-> String
serializeInstance nl inst =
let
iname = Instance.name inst
pnode = Container.nameOf nl (Instance.pNode inst)
sidx = Instance.sNode inst
snode = (if sidx == Node.noSecondary
then ""
else Container.nameOf nl sidx)
in
printf "%s|%d|%d|%d|%s|%s|%s|%s|%s|%s"
iname (Instance.mem inst) (Instance.dsk inst)
(Instance.vcpus inst) (Instance.runSt inst)
(if Instance.autoBalance inst then "Y" else "N")
pnode snode (dtToString (Instance.diskTemplate inst))
(intercalate "," (Instance.tags inst))
serializeInstances :: Node.List -> Instance.List -> String
serializeInstances nl =
unlines . map (serializeInstance nl) . Container.elems
serializeCluster :: ClusterData -> String
serializeCluster (ClusterData gl nl il ctags) =
let gdata = serializeGroups gl
ndata = serializeNodes gl nl
idata = serializeInstances nl il
in intercalate "\n" [gdata, ndata, idata, unlines ctags]
loadGroup :: (Monad m) => [String]
-> m (String, Group.Group)
loadGroup [name, gid, apol] = do
xapol <- apolFromString apol
return (gid, Group.create name gid xapol)
loadGroup s = fail $ "Invalid/incomplete group data: '" ++ show s ++ "'"
loadNode :: (Monad m) =>
NameAssoc
-> [String]
-> m (String, Node.Node)
loadNode ktg [name, tm, nm, fm, td, fd, tc, fo, gu] = do
gdx <- lookupGroup ktg name gu
new_node <-
if any (== "?") [tm,nm,fm,td,fd,tc] || fo == "Y" then
return $ Node.create name 0 0 0 0 0 0 True gdx
else do
vtm <- tryRead name tm
vnm <- tryRead name nm
vfm <- tryRead name fm
vtd <- tryRead name td
vfd <- tryRead name fd
vtc <- tryRead name tc
return $ Node.create name vtm vnm vfm vtd vfd vtc False gdx
return (name, new_node)
loadNode _ s = fail $ "Invalid/incomplete node data: '" ++ show s ++ "'"
loadInst :: NameAssoc
-> [String]
-> Result (String, Instance.Instance)
loadInst ktn [ name, mem, dsk, vcpus, status, auto_bal, pnode, snode
, dt, tags ] = do
pidx <- lookupNode ktn name pnode
sidx <- (if null snode then return Node.noSecondary
else lookupNode ktn name snode)
vmem <- tryRead name mem
vdsk <- tryRead name dsk
vvcpus <- tryRead name vcpus
auto_balance <- case auto_bal of
"Y" -> return True
"N" -> return False
_ -> fail $ "Invalid auto_balance value '" ++ auto_bal ++
"' for instance " ++ name
disk_template <- annotateResult ("Instance " ++ name) (dtFromString dt)
when (sidx == pidx) $ fail $ "Instance " ++ name ++
" has same primary and secondary node - " ++ pnode
let vtags = sepSplit ',' tags
newinst = Instance.create name vmem vdsk vvcpus status vtags
auto_balance pidx sidx disk_template
return (name, newinst)
loadInst _ s = fail $ "Invalid/incomplete instance data: '" ++ show s ++ "'"
loadTabular :: (Monad m, Element a) =>
[String]
-> ([String] -> m (String, a))
-> m ( NameAssoc
, Container.Container a )
loadTabular lines_data convert_fn = do
let rows = map (sepSplit '|') lines_data
kerows <- mapM convert_fn rows
return $ assignIndices kerows
readData :: String
-> IO String
readData = readFile
parseData :: String
-> Result ClusterData
parseData fdata = do
let flines = lines fdata
(glines, nlines, ilines, ctags) <-
case sepSplit "" flines of
[a, b, c, d] -> Ok (a, b, c, d)
xs -> Bad $ printf "Invalid format of the input file: %d sections\
\ instead of 4" (length xs)
(ktg, gl) <- loadTabular glines loadGroup
(ktn, nl) <- loadTabular nlines (loadNode ktg)
(_, il) <- loadTabular ilines (loadInst ktn)
return (ClusterData gl nl il ctags)
loadData :: String
-> IO (Result ClusterData)
loadData = fmap parseData . readData