module Ganeti.HTools.Instance
( Instance(..)
, Disk(..)
, AssocList
, List
, create
, isRunning
, isOffline
, notOffline
, instanceDown
, usesSecMem
, applyIfOnline
, setIdx
, setName
, setAlias
, setPri
, setSec
, setBoth
, setMovable
, specOf
, getTotalSpindles
, instBelowISpec
, instAboveISpec
, instMatchesPolicy
, shrinkByType
, localStorageTemplates
, hasSecondary
, requiredNodes
, allNodes
, usesLocalStorage
, mirrorType
, usesMemory
) where
import Control.Monad (liftM2)
import Ganeti.BasicTypes
import qualified Ganeti.HTools.Types as T
import qualified Ganeti.HTools.Container as Container
import Ganeti.HTools.Nic (Nic)
import Ganeti.Utils
data Disk = Disk
{ dskSize :: Int
, dskSpindles :: Maybe Int
} deriving (Show, Eq)
data Instance = Instance
{ name :: String
, alias :: String
, mem :: Int
, dsk :: Int
, disks :: [Disk]
, vcpus :: Int
, runSt :: T.InstanceStatus
, pNode :: T.Ndx
, sNode :: T.Ndx
, idx :: T.Idx
, util :: T.DynUtil
, movable :: Bool
, autoBalance :: Bool
, diskTemplate :: T.DiskTemplate
, spindleUse :: Int
, allTags :: [String]
, exclTags :: [String]
, locationScore :: Int
, arPolicy :: T.AutoRepairPolicy
, nics :: [Nic]
, forthcoming :: Bool
} deriving (Show, Eq)
instance T.Element Instance where
nameOf = name
idxOf = idx
setAlias = setAlias
setIdx = setIdx
allNames n = [name n, alias n]
isRunning :: Instance -> Bool
isRunning (Instance {runSt = T.Running}) = True
isRunning (Instance {runSt = T.ErrorUp}) = True
isRunning _ = False
isOffline :: Instance -> Bool
isOffline (Instance {runSt = T.StatusOffline}) = True
isOffline _ = False
notOffline :: Instance -> Bool
notOffline = not . isOffline
instanceDown :: Instance -> Bool
instanceDown inst | isRunning inst = False
instanceDown inst | isOffline inst = False
instanceDown _ = True
applyIfOnline :: Instance -> (a -> a) -> a -> a
applyIfOnline = applyIf . notOffline
usesSecMem :: Instance -> Bool
usesSecMem inst = notOffline inst && autoBalance inst
localStorageTemplates :: [T.DiskTemplate]
localStorageTemplates = [ T.DTDrbd8, T.DTPlain ]
movableDiskTemplates :: [T.DiskTemplate]
movableDiskTemplates =
[ T.DTDrbd8
, T.DTBlock
, T.DTSharedFile
, T.DTGluster
, T.DTRbd
, T.DTExt
]
type AssocList = [(T.Idx, Instance)]
type List = Container.Container Instance
create :: String -> Int -> Int -> [Disk] -> Int -> T.InstanceStatus
-> [String] -> Bool -> T.Ndx -> T.Ndx -> T.DiskTemplate -> Int
-> [Nic] -> Bool -> Instance
create name_init mem_init dsk_init disks_init vcpus_init run_init tags_init
auto_balance_init pn sn dt su nics_init forthcoming_init =
Instance { name = name_init
, alias = name_init
, mem = mem_init
, dsk = dsk_init
, disks = disks_init
, vcpus = vcpus_init
, runSt = run_init
, pNode = pn
, sNode = sn
, idx = 1
, util = T.baseUtil
, movable = supportsMoves dt
, autoBalance = auto_balance_init
, diskTemplate = dt
, spindleUse = su
, allTags = tags_init
, exclTags = []
, locationScore = 0
, arPolicy = T.ArNotEnabled
, nics = nics_init
, forthcoming = forthcoming_init
}
setIdx :: Instance
-> T.Idx
-> Instance
setIdx t i = t { idx = i }
setName :: Instance
-> String
-> Instance
setName t s = t { name = s, alias = s }
setAlias :: Instance
-> String
-> Instance
setAlias t s = t { alias = s }
setPri :: Instance
-> T.Ndx
-> Instance
setPri t p = t { pNode = p }
setSec :: Instance
-> T.Ndx
-> Instance
setSec t s = t { sNode = s }
setBoth :: Instance
-> T.Ndx
-> T.Ndx
-> Instance
setBoth t p s = t { pNode = p, sNode = s }
setMovable :: Instance
-> Bool
-> Instance
setMovable t m = t { movable = m }
shrinkByType :: Instance -> T.FailMode -> Result Instance
shrinkByType inst T.FailMem = let v = mem inst T.unitMem
in if v < T.unitMem
then Bad "out of memory"
else Ok inst { mem = v }
shrinkByType inst T.FailDisk =
let newdisks = [d {dskSize = dskSize d T.unitDsk}| d <- disks inst]
v = dsk inst (length . disks $ inst) * T.unitDsk
in if any (< T.unitDsk) $ map dskSize newdisks
then Bad "out of disk"
else Ok inst { dsk = v, disks = newdisks }
shrinkByType inst T.FailCPU = let v = vcpus inst T.unitCpu
in if v < T.unitCpu
then Bad "out of vcpus"
else Ok inst { vcpus = v }
shrinkByType inst T.FailSpindles =
case disks inst of
[Disk ds sp] -> case sp of
Nothing -> Bad "No spindles, shouldn't have happened"
Just sp' -> let v = sp' T.unitSpindle
in if v < T.unitSpindle
then Bad "out of spindles"
else Ok inst { disks = [Disk ds (Just v)] }
d -> Bad $ "Expected one disk, but found " ++ show d
shrinkByType _ f = Bad $ "Unhandled failure mode " ++ show f
getTotalSpindles :: Instance -> Maybe Int
getTotalSpindles inst =
foldr (liftM2 (+) . dskSpindles ) (Just 0) (disks inst)
specOf :: Instance -> T.RSpec
specOf Instance { mem = m, dsk = d, vcpus = c, disks = dl } =
let sp = case dl of
[Disk _ (Just sp')] -> sp'
_ -> 0
in T.RSpec { T.rspecCpu = c, T.rspecMem = m,
T.rspecDsk = d, T.rspecSpn = sp }
instCompareISpec :: Ordering -> Instance-> T.ISpec -> Bool -> T.OpResult ()
instCompareISpec which inst ispec exclstor
| which == mem inst `compare` T.iSpecMemorySize ispec = Bad T.FailMem
| which `elem` map ((`compare` T.iSpecDiskSize ispec) . dskSize)
(disks inst) = Bad T.FailDisk
| which == vcpus inst `compare` T.iSpecCpuCount ispec = Bad T.FailCPU
| exclstor &&
case getTotalSpindles inst of
Nothing -> True
Just sp_sum -> which == sp_sum `compare` T.iSpecSpindleUse ispec
= Bad T.FailSpindles
| not exclstor && which == spindleUse inst `compare` T.iSpecSpindleUse ispec
= Bad T.FailSpindles
| diskTemplate inst /= T.DTDiskless &&
which == length (disks inst) `compare` T.iSpecDiskCount ispec
= Bad T.FailDiskCount
| otherwise = Ok ()
instBelowISpec :: Instance -> T.ISpec -> Bool -> T.OpResult ()
instBelowISpec = instCompareISpec GT
instAboveISpec :: Instance -> T.ISpec -> Bool -> T.OpResult ()
instAboveISpec = instCompareISpec LT
instMatchesMinMaxSpecs :: Instance -> T.MinMaxISpecs -> Bool -> T.OpResult ()
instMatchesMinMaxSpecs inst minmax exclstor = do
instAboveISpec inst (T.minMaxISpecsMinSpec minmax) exclstor
instBelowISpec inst (T.minMaxISpecsMaxSpec minmax) exclstor
instMatchesSpecs :: Instance -> [T.MinMaxISpecs] -> Bool -> T.OpResult ()
instMatchesSpecs _ [] _ = Ok ()
instMatchesSpecs inst minmaxes exclstor =
foldr eithermatch (Bad T.FailInternal) minmaxes
where eithermatch mm (Bad _) = instMatchesMinMaxSpecs inst mm exclstor
eithermatch _ y@(Ok ()) = y
instMatchesPolicy :: Instance -> T.IPolicy -> Bool -> T.OpResult ()
instMatchesPolicy inst ipol exclstor = do
instMatchesSpecs inst (T.iPolicyMinMaxISpecs ipol) exclstor
if diskTemplate inst `elem` T.iPolicyDiskTemplates ipol
then Ok ()
else Bad T.FailDisk
hasSecondary :: Instance -> Bool
hasSecondary = (== T.DTDrbd8) . diskTemplate
requiredNodes :: T.DiskTemplate -> Int
requiredNodes T.DTDrbd8 = 2
requiredNodes _ = 1
allNodes :: Instance -> [T.Ndx]
allNodes inst = case diskTemplate inst of
T.DTDrbd8 -> [pNode inst, sNode inst]
_ -> [pNode inst]
usesLocalStorage :: Instance -> Bool
usesLocalStorage = (`elem` localStorageTemplates) . diskTemplate
supportsMoves :: T.DiskTemplate -> Bool
supportsMoves = (`elem` movableDiskTemplates)
mirrorType :: Instance -> T.MirrorType
mirrorType = T.templateMirrorType . diskTemplate
usesMemory :: Instance -> Bool
usesMemory inst
| forthcoming inst = False
| otherwise = case runSt inst of
T.StatusDown -> False
T.StatusOffline -> False
T.ErrorDown -> False
T.ErrorUp -> True
T.NodeDown -> True
T.NodeOffline -> True
T.Running -> True
T.UserDown -> False
T.WrongNode -> True