<commit_before>{-| Cluster rolling maintenance helper.

-}

{-

Copyright (C) 2012 Google Inc.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.

-}

module Ganeti.HTools.Program.Hroller
  ( main
  , options
  , arguments
  ) where

import Control.Applicative
import Control.Monad
import Data.Function
import Data.List
import Data.Ord

import qualified Data.IntMap as IntMap

import qualified Ganeti.HTools.Container as Container
import qualified Ganeti.HTools.Node as Node
import qualified Ganeti.HTools.Instance as Instance
import qualified Ganeti.HTools.Group as Group

import Ganeti.BasicTypes
import Ganeti.Common
import Ganeti.HTools.CLI
import Ganeti.HTools.ExtLoader
import Ganeti.HTools.Graph
import Ganeti.HTools.Loader
import Ganeti.HTools.Types
import Ganeti.Utils

-- | Options list and functions.
options :: IO [OptType]
options = do
  luxi <- oLuxiSocket
  return
    [ luxi
    , oRapiMaster
    , oDataFile
    , oIAllocSrc
    , oOfflineNode
    , oOfflineMaintenance
    , oVerbose
    , oQuiet
    , oNoHeaders
    , oNodeTags
    , oSaveCluster
    , oGroup
    , oSkipNonRedundant
    , oIgnoreNonRedundant
    , oForce
    , oOneStepOnly
    ]

-- | The list of arguments supported by the program.
arguments :: [ArgCompletion]
arguments = []

-- | Compute the result of moving an instance to a different node.
move :: Idx -> Ndx -> (Node.List, Instance.List)
        -> OpResult (Node.List, Instance.List)
move idx new_ndx (nl, il) = do
  let new_node = Container.find new_ndx nl
      inst = Container.find idx il
      old_ndx = Instance.pNode inst
      old_node = Container.find old_ndx nl
  new_node' <- Node.addPriEx True new_node inst
  let old_node' = Node.removePri old_node inst
      inst' = Instance.setPri inst new_ndx
      nl' = Container.addTwo old_ndx old_node' new_ndx new_node' nl
      il' = Container.add idx inst' il
  return (nl', il')

-- | Move an instance to one of the candidate nodes mentioned.
locateInstance :: Idx -> [Ndx] -> (Node.List, Instance.List)
                  -> Result (Node.List, Instance.List)
locateInstance idx ndxs conf =
  msum $ map (opToResult . flip (move idx) conf) ndxs

-- | Move a list of instances to some of the candidate nodes mentioned.
locateInstances :: [Idx] -> [Ndx] -> (Node.List, Instance.List)
                   -> Result (Node.List, Instance.List)
locateInstances idxs ndxs conf =
  foldM (\ cf idx -> locateInstance idx ndxs cf) conf idxs

-- | Greedily move the non-redundant instances away from a list of nodes.
-- The arguments are the list of nodes to be cleared, a list of nodes the
-- instances can be moved to, and an initial configuration. Returned is a
-- list of nodes that can be cleared simultaneously and the configuration
-- after these nodes are cleared.
clearNodes :: [Ndx] -> [Ndx] -> (Node.List, Instance.List)
              -> Result ([Ndx], (Node.List, Instance.List))
clearNodes [] _ conf = return ([], conf)
clearNodes (ndx:ndxs) targets conf = withFirst `mplus` withoutFirst where
  withFirst = do
     let othernodes = delete ndx targets
     conf' <- locateInstances (nonRedundant conf ndx) othernodes conf
     (ndxs', conf'') <- clearNodes ndxs othernodes conf'
     return (ndx:ndxs', conf'')
  withoutFirst = clearNodes ndxs targets conf

-- | Parition a list of nodes into chunks according cluster capacity.
partitionNonRedundant :: [Ndx] -> [Ndx] -> (Node.List, Instance.List)
                         -> Result [[Ndx]]
partitionNonRedundant [] _ _ = return []
partitionNonRedundant ndxs targets conf = do
  (grp, _) <- clearNodes ndxs targets conf
  guard . not . null $ grp
  let remaining = ndxs \\ grp
  part <- partitionNonRedundant remaining targets conf
  return $ grp : part

-- | Gather statistics for the coloring algorithms.
-- Returns a string with a summary on how each algorithm has performed,
-- in order of non-decreasing effectiveness, and whether it tied or lost
-- with the previous one.
getStats :: [(String, ColorVertMap)] -> String
getStats colorings = snd . foldr helper (0,"") $ algBySize colorings
    where algostat (algo, cmap) = algo ++ ": " ++ size cmap ++ grpsizes cmap
          size cmap = show (IntMap.size cmap) ++ " "
          grpsizes cmap =<commit_msg>Restrict instance moves in hroller to the same node group<commit_after>{-| Cluster rolling maintenance helper.

-}

{-

Copyright (C) 2012 Google Inc.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.

-}

module Ganeti.HTools.Program.Hroller
  ( main
  , options
  , arguments
  ) where

import Control.Applicative
import Control.Monad
import Data.Function
import Data.List
import Data.Ord

import qualified Data.IntMap as IntMap

import qualified Ganeti.HTools.Container as Container
import qualified Ganeti.HTools.Node as Node
import qualified Ganeti.HTools.Instance as Instance
import qualified Ganeti.HTools.Group as Group

import Ganeti.BasicTypes
import Ganeti.Common
import Ganeti.HTools.CLI
import Ganeti.HTools.ExtLoader
import Ganeti.HTools.Graph
import Ganeti.HTools.Loader
import Ganeti.HTools.Types
import Ganeti.Utils

-- | Options list and functions.
options :: IO [OptType]
options = do
  luxi <- oLuxiSocket
  return
    [ luxi
    , oRapiMaster
    , oDataFile
    , oIAllocSrc
    , oOfflineNode
    , oOfflineMaintenance
    , oVerbose
    , oQuiet
    , oNoHeaders
    , oNodeTags
    , oSaveCluster
    , oGroup
    , oSkipNonRedundant
    , oIgnoreNonRedundant
    , oForce
    , oOneStepOnly
    ]

-- | The list of arguments supported by the program.
arguments :: [ArgCompletion]
arguments = []

-- | Compute the result of moving an instance to a different node.
move :: Idx -> Ndx -> (Node.List, Instance.List)
        -> OpResult (Node.List, Instance.List)
move idx new_ndx (nl, il) = do
  let new_node = Container.find new_ndx nl
      inst = Container.find idx il
      old_ndx = Instance.pNode inst
      old_node = Container.find old_ndx nl
  new_node' <- Node.addPriEx True new_node inst
  let old_node' = Node.removePri old_node inst
      inst' = Instance.setPri inst new_ndx
      nl' = Container.addTwo old_ndx old_node' new_ndx new_node' nl
      il' = Container.add idx inst' il
  return (nl', il')

-- | Move an instance to one of the candidate nodes mentioned.
locateInstance :: Idx -> [Ndx] -> (Node.List, Instance.List)
                  -> Result (Node.List, Instance.List)
locateInstance idx ndxs conf =
  msum $ map (opToResult . flip (move idx) conf) ndxs

-- | Move a list of instances to some of the candidate nodes mentioned.
locateInstances :: [Idx] -> [Ndx] -> (Node.List, Instance.List)
                   -> Result (Node.List, Instance.List)
locateInstances idxs ndxs conf =
  foldM (\ cf idx -> locateInstance idx ndxs cf) conf idxs

-- | Greedily move the non-redundant instances away from a list of nodes.
-- The arguments are the list of nodes to be cleared, a list of nodes the
-- instances can be moved to, and an initial configuration. Returned is a
-- list of nodes that can be cleared simultaneously and the configuration
-- after these nodes are cleared.
clearNodes :: [Ndx] -> [Ndx] -> (Node.List, Instance.List)
              -> Result ([Ndx], (Node.List, Instance.List))
clearNodes [] _ conf = return ([], conf)
clearNodes (ndx:ndxs) targets conf@(nl, _) =
  withFirst `mplus` withoutFirst where
  withFirst = do
     let othernodes = delete ndx targets
         grp = Node.group $ Container.find ndx nl
         othernodesSameGroup =
           filter ((==) grp . Node.group . flip Container.find nl) othernodes
     conf' <- locateInstances (nonRedundant conf ndx) othernodesSameGroup conf
     (ndxs', conf'') <- clearNodes ndxs othernodes conf'
     return (ndx:ndxs', conf'')
  withoutFirst = clearNodes ndxs targets conf

-- | Parition a list of nodes into chunks according cluster capacity.
partitionNonRedundant :: [Ndx] -> [Ndx] -> (Node.List, Instance.List)
                         -> Result [[Ndx]]
partitionNonRedundant [] _ _ = return []
partitionNonRedundant ndxs targets conf = do
  (grp, _) <- clearNodes ndxs targets conf
  guard . not . null $ grp
  let remaining = ndxs \\ grp
  part <- partitionNonRedundant remaining targets conf
  return $ grp : part

-- | Gather statistics for the coloring algorithms.
-- Returns a string with a summary on how each algorithm has performed,
-- in order of non-decreasing effectiveness, and whether it tied or lost
-- with the previous one.
getStats :: [(String, ColorVertMap)] -> String
getStats colorings = snd . foldr helper (0,"") $ algBySize colorings
    where algostat (algo, cmap) = algo ++ ": " ++ size cmap ++ grpsizes cmap
          size cmap = show (IntMap.size cmap) ++ " "
          grpsizes cmap =
{'fraction_non_alphanumeric': 0.08523300383296349, 'fraction_numerical': 0.00342949364535001, 'mean_word_length': 3.275549805950841, 'pattern_counts': {'":': 2, '<': 15, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3814713', 'n_tokens_mistral': 2971, 'n_tokens_neox': 2934, 'n_words': 1144}