packages feed

batchd-libvirt-0.1.0.0: src/Batchd/Ext/LibVirt.hs

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | This module contains an implementation of batchd host controller,
-- which controls VMs supported by LibVirt.
module Batchd.Ext.LibVirt
  (
    LibVirtSettings (..),
    libVirtDriver
  ) where

import Control.Monad (when)
import Control.Exception
import Control.Concurrent
import Data.Time
import qualified Data.Text as T
import Data.Text.Format.Heavy
import Data.Aeson
import System.Log.Heavy
import Batchd.Core
import System.LibVirt as V

-- | Settings of LibVirt host controller
data LibVirtSettings = LibVirtSettings {
    lvEnableStartStop :: Bool      -- ^ Automatic start\/stop of VMs can be disabled in config file.
  , lvConnectionString :: String   -- ^ Libvirt connection string. Default is @"qemu:///system"@.
  }
  deriving (Show)

instance FromJSON LibVirtSettings where
  parseJSON (Object v) = do
    driver <- v .: "driver"
    when (driver /= ("libvirt" :: T.Text)) $
      fail $ "incorrect driver specification"
    enable <- v .:? "enable_start_stop" .!= True
    conn <- v .:? "connection_string" .!= "qemu:///system"
    return $ LibVirtSettings enable conn

-- | Initialize LibVirt host controller
libVirtDriver :: HostDriver
libVirtDriver = controllerFromConfig "libvirt" mkLibVirt

mkLibVirt :: LibVirtSettings -> LoggingTState -> HostController
mkLibVirt l lts = HostController {

  controllerDriverName = driverName libVirtDriver,

  doesSupportStartStop = lvEnableStartStop l,

  getActualHostName = \_ -> return Nothing,

  startHost = \host -> do
    let name = T.unpack $ hControllerId host
    withConnection (lvConnectionString l) $ \conn -> do
        infoIO lts $(here) "Connection to libvirt URI {} succeeded" (Single $ lvConnectionString l)
        mbdom <- do
                 x <- try $ lookupDomainName conn name
                 case x of
                   Left (e :: V.Error) -> do
                                          reportErrorIO lts $(here) "Cannot get domain ID by name `{}': {}" (name, show e)
                                          return Nothing
                   Right dom -> return (Just dom)
        case mbdom of
          Just dom -> do
            di <- getDomainInfo dom
            debugIO lts $(here) "Domain information obtained: {}" (Single $ show di)
            case diState di of
              DomainRunning -> return $ Right ()
              DomainPaused -> resumeDomain dom >> (return $ Right ())
              DomainShutoff -> do
                       createDomain dom
                       return $ Right ()
              st -> return $ Left $ UnknownError $ "Don't know what to do with virtual domain " ++ name ++ " in state " ++ show st
          Nothing -> return $ Left $ UnknownError $ "Domain is not defined in hypervisor: " ++ name ,

  stopHost = \name -> do
    withConnection (lvConnectionString l) $ \conn -> do
        infoIO lts $(here) "Connection to libvirt URI {} succeeded" (Single $ lvConnectionString l)
        mbdom <- do
                 x <- try $ lookupDomainName conn (T.unpack name)
                 case x of
                   Left (e :: V.Error) -> do
                                          reportErrorIO lts $(here) "Cannot get domain ID by name `{}': {}" (name, show e)
                                          return Nothing
                   Right dom -> return (Just dom)
        case mbdom of
          Nothing -> return $ Left $ UnknownError $ "No such domain on hypervisor: " ++ T.unpack name
          Just dom -> do
            shutdownDomain dom
            return $ Right ()
  }