-- | Balance a single group, restricted to the allowed nodes and
-- minimal gain.
balanceGroup :: IORef MemoryState
             -> Set.Set String
             -> L.Client
             -> Set.Set Int
             -> Double
             -> (Int,  (Node.List, Instance.List))
             -> ResultT String IO [JobId]
balanceGroup memstate xens client allowedNodes threshold (gidx, (nl, il)) = do
  logDebug $ printf "Balancing group %d, %d nodes, %d instances." gidx
               (Container.size nl) (Container.size il)
  let ini_cv = Metrics.compCV nl
      ini_tbl = Cluster.Table nl il ini_cv []
      opts = defaultOptions { algAllowedNodes = Just allowedNodes
                            , algMinGain = threshold
                            , algMinGainLimit = 10 * threshold
                            }
      cmds = iterateBalance opts ini_tbl []
      tasks = take 1 $ Cluster.splitJobs cmds
  logDebug $ "First task group: " ++ show tasks
  now <- liftIO currentTimestamp
  let jobs = tasks >>= map (moveToJob now (nl, il))
      evacs = filter (`Set.member` xens)
              (concat tasks >>= evacuatedInsts (nl, il))
  if null jobs
    then return []
    else do
      unless (null evacs) $ do
        logDebug $ "Evacuation of instances " ++ show evacs
        liftIO $ addEvacuated memstate evacs
      jids <- liftIO $ submitJobs jobs client
      case jids of
        Bad e -> mkResultT . logAndBad
                   $ "Failure submitting balancing jobs: " ++ e
        Ok jids' -> return jids'

-- * Interface function

-- | Carry out all the needed balancing, based on live CPU data, only touching
-- the available nodes. Only carry out balancing steps where the gain is above
-- the threshold.
balanceGroup :: IORef MemoryState
             -> Set.Set String
             -> L.Client
             -> Set.Set Int
             -> Double
             -> (Int,  (Node.List, Instance.List))
             -> ResultT String IO [JobId]
balanceGroup memstate xens client allowedNodes threshold (gidx, (nl, il)) = do
  logDebug $ printf "Balancing group %d, %d nodes, %d instances." gidx
               (Container.size nl) (Container.size il)
  let ini_cv = Metrics.compCV nl
      ini_tbl = Cluster.Table nl il ini_cv []
      opts = defaultOptions { algAllowedNodes = Just allowedNodes
                            , algMinGain = threshold
                            , algMinGainLimit = 10 * threshold
                            }
      cmds = iterateBalance opts ini_tbl []
      tasks = take 1 $ Cluster.splitJobs cmds
  logDebug $ "First task group: " ++ show tasks
  now <- liftIO currentTimestamp
  let jobs = tasks >>= map (moveToJob now (nl, il))
      evacs = filter (`Set.member` xens)
              (concat tasks >>= evacuatedInsts (nl, il))
  if null jobs
    then return []
    else do
      unless (null evacs) $ do
        logDebug $ "Evacuation of instances " ++ show evacs
        liftIO $ addEvacuated memstate evacs
      jids <- liftIO $ submitJobs jobs client
      case jids of
        Bad e -> mkResultT . logAndBad
                   $ "Failure submitting balancing jobs: " ++ e
        Ok jids' -> return jids'

-- * Interface function

-- | Carry out all the needed balancing, based on live CPU data, only touching
-- the available nodes. Only carry out balancing steps where the gain is above
-- the threshold.
balanceGroup memstate xens client allowedNodes threshold (gidx, (nl, il)) = do
  logDebug $ printf "Balancing group %d, %d nodes, %d instances." gidx
               (Container.size nl) (Container.size il)
  let ini_cv = Metrics.compCV nl
      ini_tbl = Cluster.Table nl il ini_cv []
      opts = defaultOptions { algAllowedNodes = Just allowedNodes
                            , algMinGain = threshold
                            , algMinGainLimit = 10 * threshold
                            }
      cmds = iterateBalance opts ini_tbl []
      tasks = take 1 $ Cluster.splitJobs cmds
  logDebug $ "First task group: " ++ show tasks
  now <- liftIO currentTimestamp
  let jobs = tasks >>= map (moveToJob now (nl, il))
      evacs = filter (`Set.member` xens)
              (concat tasks >>= evacuatedInsts (nl, il))
  if null jobs
    then return []
    else do
      unless (null evacs) $ do
        logDebug $ "Evacuation of instances " ++ show evacs
        liftIO $ addEvacuated memstate evacs
      jids <- liftIO $ submitJobs jobs client
      case jids of
        Bad e -> mkResultT . logAndBad
                   $ "Failure submitting balancing jobs: " ++ e
        Ok jids' -> return jids'

-- * Interface function

-- | Carry out all the needed balancing, based on live CPU data, only touching
-- the available nodes. Only carry out balancing steps where the gain is above
-- the threshold.