module Propellor.Property.Lvm (
lvFormatted,
installed,
Eep(..),
VolumeGroup(..),
LogicalVolume(..),
) where
import Propellor
import Propellor.Base
import Utility.DataUnits
import qualified Propellor.Property.Apt as Apt
import qualified Propellor.Property.Mount as Mount
import qualified Propellor.Property.Partition as Partition
data Eep = YesReallyFormatLogicalVolume
type DataSize = String
newtype VolumeGroup = VolumeGroup String
data LogicalVolume = LogicalVolume String VolumeGroup
lvFormatted
:: Eep
-> LogicalVolume
-> DataSize
-> Partition.Fs
-> RevertableProperty DebianLike UnixLike
lvFormatted YesReallyFormatLogicalVolume lv sz fs =
setup <!> cleanup
where
setup :: Property DebianLike
setup = property' ("formatted logical volume " ++ (vglv lv)) $ \w -> do
es <- liftIO $ vgExtentSize vg
case es of
Nothing -> errorMessage $
"can not get extent size, does volume group "
++ vgname ++ " exist?"
Just extentSize -> do
case parseSize of
Nothing -> errorMessage
"can not parse volume group size"
Just size -> do
state <- liftIO $ lvState lv
let rsize = roundSize extentSize size
ensureProperty w $
setupprop rsize state
cleanup :: Property UnixLike
cleanup = property' ("removed logical volume " ++ (vglv lv)) $ \w -> do
exists <- liftIO $ lvExists lv
ensureProperty w $ if exists
then removedprop
else doNothing
parseSize :: Maybe Integer
parseSize = readSize dataUnits sz
roundSize :: Integer -> Integer -> Integer
roundSize extentSize s =
(s + extentSize - 1) `div` extentSize * extentSize
setupprop :: Integer -> (Maybe LvState) -> Property DebianLike
setupprop size Nothing = createdprop size `before` formatprop
setupprop size (Just (LvState csize cfs))
| size == csize && fsMatch fs cfs = doNothing
| size == csize = formatprop
| fsMatch fs cfs = tightenTargets $ resizedprop size True
| otherwise = resizedprop size False `before` formatprop
createdprop :: Integer -> Property UnixLike
createdprop size =
cmdProperty "lvcreate"
(bytes size $ [ "-n", lvname, "--yes", vgname ])
`assume` MadeChange
resizedprop :: Integer -> Bool -> Property UnixLike
resizedprop size rfs =
cmdProperty "lvresize"
(resizeFs rfs $ bytes size $ [ vglv lv ])
`assume` MadeChange
where
resizeFs True l = "-r" : l
resizeFs False l = l
removedprop :: Property UnixLike
removedprop = cmdProperty "lvremove" [ "-f", vglv lv ]
`assume` MadeChange
formatprop :: Property DebianLike
formatprop = Partition.formatted Partition.YesReallyFormatPartition
fs (path lv)
fsMatch :: Partition.Fs -> Maybe Partition.Fs -> Bool
fsMatch a (Just b) = a == b
fsMatch _ _ = False
bytes size l = "-L" : ((show size) ++ "b") : l
(LogicalVolume lvname vg@(VolumeGroup vgname)) = lv
installed :: RevertableProperty DebianLike DebianLike
installed = install <!> remove
where
install = Apt.installed ["lvm2"]
remove = Apt.removed ["lvm2"]
data LvState = LvState Integer (Maybe Partition.Fs)
lvExists :: LogicalVolume -> IO Bool
lvExists lv = doesFileExist (path lv)
lvState :: LogicalVolume -> IO (Maybe LvState)
lvState lv = do
exists <- lvExists lv
if not exists
then return Nothing
else do
s <- readLvSize
fs <- maybe Nothing Partition.parseFs <$> readFs
return $ do
size <- s
return $ LvState size fs
where
readLvSize = catchDefaultIO Nothing $ readish
<$> readProcess "lvs" [ "-o", "size", "--noheadings",
"--nosuffix", "--units", "b", vglv lv ]
readFs = Mount.blkidTag "TYPE" (path lv)
vgExtentSize :: VolumeGroup -> IO (Maybe Integer)
vgExtentSize (VolumeGroup vgname) =
catchDefaultIO Nothing $ readish
<$> readProcess "vgs" [ "-o", "vg_extent_size",
"--noheadings", "--nosuffix", "--units", "b", vgname ]
vglv :: LogicalVolume -> String
vglv lv =
vgname </> lvname
where
(LogicalVolume lvname (VolumeGroup vgname)) = lv
path :: LogicalVolume -> FilePath
path lv = "/dev" </> (vglv lv)