    -- | Maintainer: Sean Whitton <spwhitton@spwhitton.name>

    module Propellor.Property.Libvirt (
    	NumVCPUs(..),
    	MiBMemory(..),
    	AutoStart(..),
    	DiskImageType(..),
    	installed,
    	defaultNetworkAutostarted,
    	defaultNetworkStarted,
    	defined,
    ) where

    import Propellor.Base
    import Propellor.Types.Info
    import Propellor.Property.Chroot
    import Propellor.Property.DiskImage
    import qualified Propellor.Property.Apt as Apt

    import Utility.Split

    -- | The number of virtual CPUs to assign to the virtual machine
    newtype NumVCPUs = NumVCPUs Int

    -- | The number of MiB of memory to assign to the virtual machine
    newtype MiBMemory = MiBMemory Int

    -- | Whether the virtual machine should be started after it is defined, and at
    -- host system boot
    data AutoStart = AutoStart | NoAutoStart

    -- | Which type of disk image to build for the virtual machine
    data DiskImageType = Raw -- TODO: | QCow2

    -- | Install basic libvirt components
    installed :: Property DebianLike
    installed :: Property DebianLike
    installed = [Package] -> Property DebianLike
    Apt.installed ["libvirt-clients", "virtinst", "libvirt-daemon", "libvirt-daemon-system"]

    -- | Ensure that the default libvirt network is set to autostart, and start it.
    --
    -- On Debian, it is not started by default after installation of libvirt.
    defaultNetworkAutostarted :: Property DebianLike
    defaultNetworkAutostarted :: Property DebianLike
    defaultNetworkAutostarted = Property UnixLike
    autostarted
    	Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    installed
    	Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Property DebianLike
    defaultNetworkStarted
      where
    	autostarted :: Property UnixLike
    autostarted = IO Bool -> UncheckedProperty UnixLike -> Property UnixLike
    forall (p :: * -> *) i (m :: * -> *).
    (Checkable p i, LiftPropellor m) =>
    m Bool -> p i -> Property i
    check (Bool -> Bool
    not (Bool -> Bool) -> IO Bool -> IO Bool
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Package -> IO Bool
    doesFileExist Package
    autostartFile) (UncheckedProperty UnixLike -> Property UnixLike)
    -> UncheckedProperty UnixLike -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $
    		Package -> [Package] -> UncheckedProperty UnixLike
    cmdProperty "virsh" ["net-autostart", "default"]
    	autostartFile :: Package
    autostartFile = "/etc/libvirt/qemu/networks/autostart/default.xml"

    -- | Ensure that the default libvirt network is started.
    defaultNetworkStarted :: Property DebianLike
    defaultNetworkStarted :: Property DebianLike
    defaultNetworkStarted =	Property UnixLike
    go Property UnixLike
    -> Property DebianLike
    -> CombinedType (Property UnixLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    installed
      where
    	go :: Property UnixLike
    	go :: Property UnixLike
    go = Package -> Propellor Result -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Package -> Propellor Result -> Property (MetaTypes metatypes)
    property "start libvirt's default network" (Propellor Result -> Property UnixLike)
    -> Propellor Result -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $ do
    		[[Package]]
    runningNetworks <- IO [[Package]] -> Propellor [[Package]]
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO [[Package]] -> Propellor [[Package]])
    -> IO [[Package]] -> Propellor [[Package]]
    forall a b. (a -> b) -> a -> b
    $ [Package] -> IO [[Package]]
    virshGetColumns ["net-list"]
    		if ["default"] [Package] -> [[Package]] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` (Int -> [Package] -> [Package]
    forall a. Int -> [a] -> [a]
    take 1 ([Package] -> [Package]) -> [[Package]] -> [[Package]]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> [[Package]]
    runningNetworks)
    			then Propellor Result
    noChange
    			else IO () -> Propellor Result
    makeChange (IO () -> Propellor Result) -> IO () -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ IO Bool -> IO () -> IO ()
    forall (m :: * -> *). Monad m => m Bool -> m () -> m ()
    unlessM IO Bool
    startIt (IO () -> IO ()) -> IO () -> IO ()
    forall a b. (a -> b) -> a -> b
    $
    				Package -> IO ()
    forall (m :: * -> *) a. MonadIO m => Package -> m a
    errorMessage "failed to start default network"
    	startIt :: IO Bool
    startIt = Package -> [CommandParam] -> IO Bool
    boolSystem "virsh" [Package -> CommandParam
    Param "net-start", Package -> CommandParam
    Param "default"]

    -- | Builds a disk image with the properties of the given Host, installs a
    -- libvirt configuration file to boot the image, and if it is set to autostart,
    -- start the VM.
    --
    -- Note that building the disk image happens only once.  So if you change the
    -- properties of the given Host, this property will not modify the disk image.
    -- In order to later apply properties to the VM, you should spin it directly, or
    -- arrange to have it spun with a property like 'Cron.runPropellor', or use
    -- 'Propellor.Property.Conductor' from the VM host.
    --
    -- Suggested usage in @config.hs@:
    --
    -- > mybox = host "mybox.example.com" $ props
    -- > 	& osDebian (Stable "stretch") X86_64
    -- > 	& Libvirt.defaultNetworkAutostarted
    -- > 	& Libvirt.defined Libvirt.Raw
    -- > 		(Libvirt.MiBMemory 2048) (Libvirt.NumVCPUs 2)
    -- > 		Libvirt.NoAutoStart subbox
    -- >
    -- > subbox = host "subbox.mybox.example.com" $ props
    -- > 	& osDebian Unstable X86_64
    -- > 	& hasPartition
    -- > 		( partition EXT4
    -- > 			`mountedAt` "/"
    -- > 			`addFreeSpace` MegaBytes 10240
    -- > 		)
    -- > 	& Apt.installed ["linux-image-amd64"]
    -- > 	& Grub.installed PC
    -- >
    -- > 	& ipv4 "192.168.122.31"
    -- > 	& Network.static "ens3" (IPv4 "192.168.122.31")
    -- > 		(Just (Network.Gateway (IPv4 "192.168.122.1")))
    -- > 		`requires` Network.cleanInterfacesFile
    -- > 	& Hostname.sane
    defined
    	:: DiskImageType
    	-> MiBMemory
    	-> NumVCPUs
    	-> AutoStart
    	-> Host
    	-> Property (HasInfo + DebianLike)
    defined :: DiskImageType
    -> MiBMemory
    -> NumVCPUs
    -> AutoStart
    -> Host
    -> Property (HasInfo + DebianLike)
    defined imageType :: DiskImageType
    imageType (MiBMemory mem :: Int
    mem) (NumVCPUs cpus :: Int
    cpus) auto :: AutoStart
    auto h :: Host
    h =
    	(Property
      (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    Property (HasInfo + DebianLike)
    built Property
      (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> CombinedType
         (Property
            (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
         (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Property UnixLike
    nuked Property
      (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> CombinedType
         (Property
            (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
         (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Property UnixLike
    xmlDefined Property
      (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property UnixLike
    -> CombinedType
         (Property
            (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
         (Property UnixLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before` Property UnixLike
    started)
    	Property
      (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property DebianLike
    -> CombinedType
         (Property
            (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
         (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    installed
      where
    	built :: Property (HasInfo + DebianLike)
    	built :: Property (HasInfo + DebianLike)
    built = IO Bool
    -> Property
         (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property
         (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall (p :: * -> *) i (m :: * -> *).
    (Checkable p i, LiftPropellor m) =>
    m Bool -> p i -> Property i
    check (Bool -> Bool
    not (Bool -> Bool) -> IO Bool -> IO Bool
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Package -> IO Bool
    doesFileExist Package
    imageLoc) (Property
       (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
     -> Property (HasInfo + DebianLike))
    -> Property
         (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    -> Property (HasInfo + DebianLike)
    forall a b. (a -> b) -> a -> b
    $
    		RevertableProperty
      (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
      Linux
    -> Property
         (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall setupmetatypes undometatypes.
    RevertableProperty setupmetatypes undometatypes
    -> Property setupmetatypes
    setupRevertableProperty (RevertableProperty
       (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
       Linux
     -> Property
          (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    -> RevertableProperty
         (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
         Linux
    -> Property
         (Sing '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish])
    forall a b. (a -> b) -> a -> b
    $ Host
    -> RawDiskImage
    -> Debootstrapped
    -> RevertableProperty (HasInfo + DebianLike) Linux
    forall d bootstrapper.
    (DiskImage d, ChrootBootstrapper bootstrapper) =>
    Host
    -> d
    -> bootstrapper
    -> RevertableProperty (HasInfo + DebianLike) Linux
    imageBuiltFor Host
    h
    			(RawDiskImage
    image) (DebootstrapConfig -> Debootstrapped
    Debootstrapped DebootstrapConfig
    forall a. Monoid a => a
    mempty)

    	nuked :: Property UnixLike
    	nuked :: Property UnixLike
    nuked = RawDiskImage -> Property UnixLike
    forall d. DiskImage d => d -> Property UnixLike
    imageChrootNotPresent RawDiskImage
    image

    	xmlDefined :: Property UnixLike
    	xmlDefined :: Property UnixLike
    xmlDefined = IO Bool -> Property UnixLike -> Property UnixLike
    forall (p :: * -> *) i (m :: * -> *).
    (Checkable p i, LiftPropellor m) =>
    m Bool -> p i -> Property i
    check (Bool -> Bool
    not (Bool -> Bool) -> IO Bool -> IO Bool
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Package -> IO Bool
    doesFileExist Package
    conf) (Property UnixLike -> Property UnixLike)
    -> Property UnixLike -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $
    		Package -> Propellor Result -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Package -> Propellor Result -> Property (MetaTypes metatypes)
    property "define the libvirt VM" (Propellor Result -> Property UnixLike)
    -> Propellor Result -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $
    		Package
    -> (Package -> Handle -> Propellor Result) -> Propellor Result
    forall (m :: * -> *) a.
    (MonadIO m, MonadMask m) =>
    Package -> (Package -> Handle -> m a) -> m a
    withTmpFile (Host -> Package
    hostName Host
    h) ((Package -> Handle -> Propellor Result) -> Propellor Result)
    -> (Package -> Handle -> Propellor Result) -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ \t :: Package
    t fh :: Handle
    fh -> do
    			Package
    xml <- IO Package -> Propellor Package
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO Package -> Propellor Package)
    -> IO Package -> Propellor Package
    forall a b. (a -> b) -> a -> b
    $ Package -> [Package] -> IO Package
    readProcess "virt-install" ([Package] -> IO Package) -> [Package] -> IO Package
    forall a b. (a -> b) -> a -> b
    $
    				[ "-n", Host -> Package
    hostName Host
    h
    				, "--memory=" Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ Int -> Package
    forall a. Show a => a -> Package
    show Int
    mem
    				, "--vcpus=" Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ Int -> Package
    forall a. Show a => a -> Package
    show Int
    cpus
    				, "--disk"
    				, "path=" Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ Package
    imageLoc
    					Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ ",device=disk,bus=virtio"
    				, "--print-xml"
    				] [Package] -> [Package] -> [Package]
    forall a. [a] -> [a] -> [a]
    ++ [Package]
    autoStartArg [Package] -> [Package] -> [Package]
    forall a. [a] -> [a] -> [a]
    ++ [Package]
    osVariantArg
    			IO () -> Propellor ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO () -> Propellor ()) -> IO () -> Propellor ()
    forall a b. (a -> b) -> a -> b
    $ Handle -> Package -> IO ()
    hPutStrLn Handle
    fh Package
    xml
    			IO () -> Propellor ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO () -> Propellor ()) -> IO () -> Propellor ()
    forall a b. (a -> b) -> a -> b
    $ Handle -> IO ()
    hClose Handle
    fh
    			IO () -> Propellor Result
    makeChange (IO () -> Propellor Result) -> IO () -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ IO Bool -> IO () -> IO ()
    forall (m :: * -> *). Monad m => m Bool -> m () -> m ()
    unlessM (Package -> IO Bool
    defineIt Package
    t) (IO () -> IO ()) -> IO () -> IO ()
    forall a b. (a -> b) -> a -> b
    $
    				Package -> IO ()
    forall (m :: * -> *) a. MonadIO m => Package -> m a
    errorMessage "failed to define VM"
    	  where
    		defineIt :: Package -> IO Bool
    defineIt t :: Package
    t = Package -> [CommandParam] -> IO Bool
    boolSystem "virsh" [Package -> CommandParam
    Param "define", Package -> CommandParam
    Param Package
    t]

    	started :: Property UnixLike
    	started :: Property UnixLike
    started = case AutoStart
    auto of
    		AutoStart -> Package -> Propellor Result -> Property UnixLike
    forall k (metatypes :: k).
    SingI metatypes =>
    Package -> Propellor Result -> Property (MetaTypes metatypes)
    property "start the VM" (Propellor Result -> Property UnixLike)
    -> Propellor Result -> Property UnixLike
    forall a b. (a -> b) -> a -> b
    $ do
    			[[Package]]
    runningVMs <- IO [[Package]] -> Propellor [[Package]]
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO [[Package]] -> Propellor [[Package]])
    -> IO [[Package]] -> Propellor [[Package]]
    forall a b. (a -> b) -> a -> b
    $ [Package] -> IO [[Package]]
    virshGetColumns ["list"]
    			-- From the point of view of `virsh start`, the "State"
    			-- column in the output of `virsh list` is not relevant.
    			-- So long as the VM is listed, it's considered started.
    			if [Host -> Package
    hostName Host
    h] [Package] -> [[Package]] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` (Int -> [Package] -> [Package]
    forall a. Int -> [a] -> [a]
    take 1 ([Package] -> [Package])
    -> ([Package] -> [Package]) -> [Package] -> [Package]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Int -> [Package] -> [Package]
    forall a. Int -> [a] -> [a]
    drop 1 ([Package] -> [Package]) -> [[Package]] -> [[Package]]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> [[Package]]
    runningVMs)
    				then Propellor Result
    noChange
    				else IO () -> Propellor Result
    makeChange (IO () -> Propellor Result) -> IO () -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ IO Bool -> IO () -> IO ()
    forall (m :: * -> *). Monad m => m Bool -> m () -> m ()
    unlessM IO Bool
    startIt (IO () -> IO ()) -> IO () -> IO ()
    forall a b. (a -> b) -> a -> b
    $
    					Package -> IO ()
    forall (m :: * -> *) a. MonadIO m => Package -> m a
    errorMessage "failed to start VM"
    		NoAutoStart -> Property UnixLike
    forall k (t :: k). SingI t => Property (MetaTypes t)
    doNothing
    	  where
    		startIt :: IO Bool
    startIt = Package -> [CommandParam] -> IO Bool
    boolSystem "virsh" [Package -> CommandParam
    Param "start", Package -> CommandParam
    Param (Package -> CommandParam) -> Package -> CommandParam
    forall a b. (a -> b) -> a -> b
    $ Host -> Package
    hostName Host
    h]

    	image :: RawDiskImage
    image = case DiskImageType
    imageType of
    		Raw -> Package -> RawDiskImage
    RawDiskImage Package
    imageLoc
    	imageLoc :: Package
    imageLoc =
    		"/var/lib/libvirt/images" Package -> Package -> Package
    </> Host -> Package
    hostName Host
    h Package -> Package -> Package
    <.> case DiskImageType
    imageType of
    			Raw -> "img"
    	conf :: Package
    conf = "/etc/libvirt/qemu" Package -> Package -> Package
    </> Host -> Package
    hostName Host
    h Package -> Package -> Package
    <.> "xml"

    	osVariantArg :: [Package]
    osVariantArg = [Package] -> (Package -> [Package]) -> Maybe Package -> [Package]
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe [] (\v :: Package
    v -> ["--os-variant=" Package -> Package -> Package
    forall a. [a] -> [a] -> [a]
    ++ Package
    v]) (Maybe Package -> [Package]) -> Maybe Package -> [Package]
    forall a b. (a -> b) -> a -> b
    $ Host -> Maybe Package
    osVariant Host
    h
    	autoStartArg :: [Package]
    autoStartArg = case AutoStart
    auto of
    		AutoStart -> ["--autostart"]
    		NoAutoStart -> []

    -- ==== utility functions ====

    -- The --os-variant property is optional, per virt-install(1), so return Nothing
    -- if there isn't a known correct value.  The VM will still be defined.  Pass
    -- the value if we can, though, to optimise the generated XML for the host's OS
    osVariant :: Host -> Maybe String
    osVariant :: Host -> Maybe Package
    osVariant h :: Host
    h = Host -> Maybe System
    hostSystem Host
    h Maybe System -> (System -> Maybe Package) -> Maybe Package
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \s :: System
    s -> case System
    s of
    	System (Debian _ (Stable "jessie")) _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "debian8"
    	System (Debian _ (Stable "stretch")) _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "debian9"
    	System (Debian _ Testing) _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "debiantesting"
    	System (Debian _ Unstable) _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "debiantesting"

    	System (Buntish "trusty") _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "ubuntu14.04"
    	System (Buntish "utopic") _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "ubuntu14.10"
    	System (Buntish "vivid") _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "ubuntu15.04"
    	System (Buntish "wily") _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "ubuntu15.10"
    	System (Buntish "xenial") _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "ubuntu16.04"
    	System (Buntish "yakkety") _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "ubuntu16.10"
    	System (Buntish "zesty") _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "ubuntu17.04"
    	System (Buntish "artful") _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "ubuntu17.10"
    	System (Buntish "bionic") _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "ubuntu18.04"

    	System (FreeBSD (FBSDProduction FBSD101)) _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "freebsd10.1"
    	System (FreeBSD (FBSDProduction FBSD102)) _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "freebsd10.2"
    	System (FreeBSD (FBSDProduction FBSD093)) _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "freebsd9.3"
    	System (FreeBSD (FBSDLegacy FBSD101)) _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "freebsd10.1"
    	System (FreeBSD (FBSDLegacy FBSD102)) _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "freebsd10.2"
    	System (FreeBSD (FBSDLegacy FBSD093)) _ -> Package -> Maybe Package
    forall a. a -> Maybe a
    Just "freebsd9.3"

    	-- libvirt doesn't have an archlinux variant yet, it seems
    	System ArchLinux _ -> Maybe Package
    forall a. Maybe a
    Nothing

    	-- other stable releases that we don't know about (since there are
    	-- infinitely many possible stable release names, as it is a freeform
    	-- string, we need this to avoid a compiler warning)
    	System (Debian _ _) _ -> Maybe Package
    forall a. Maybe a
    Nothing
    	System (Buntish _) _ -> Maybe Package
    forall a. Maybe a
    Nothing

    -- Run a virsh command with the given list of arguments, that is expected to
    -- yield tabular output, and return the rows
    virshGetColumns :: [String] -> IO [[String]]
    virshGetColumns :: [Package] -> IO [[Package]]
    virshGetColumns args :: [Package]
    args = (Package -> [Package]) -> [Package] -> [[Package]]
    forall a b. (a -> b) -> [a] -> [b]
    map ((Package -> Bool) -> [Package] -> [Package]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Bool -> Bool
    not (Bool -> Bool) -> (Package -> Bool) -> Package -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Package -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null) ([Package] -> [Package])
    -> (Package -> [Package]) -> Package -> [Package]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Package -> Package -> [Package]
    forall a. Eq a => [a] -> [a] -> [[a]]
    split " ") ([Package] -> [[Package]])
    -> (Package -> [Package]) -> Package -> [[Package]]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Int -> [Package] -> [Package]
    forall a. Int -> [a] -> [a]
    drop 2 ([Package] -> [Package])
    -> (Package -> [Package]) -> Package -> [Package]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Package -> [Package]
    lines
     	(Package -> [[Package]]) -> IO Package -> IO [[Package]]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Package -> [Package] -> IO Package
    readProcess "virsh" [Package]
    args

    hostSystem :: Host -> Maybe System
    hostSystem :: Host -> Maybe System
    hostSystem = InfoVal System -> Maybe System
    forall v. InfoVal v -> Maybe v
    fromInfoVal (InfoVal System -> Maybe System)
    -> (Host -> InfoVal System) -> Host -> Maybe System
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Info -> InfoVal System
    forall v. IsInfo v => Info -> v
    fromInfo (Info -> InfoVal System)
    -> (Host -> Info) -> Host -> InfoVal System
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Host -> Info
    hostInfo