Generalise the node/instance listing
[ganeti-local] / Ganeti / HTools / Cluster.hs
index e4c96c7..ab21b32 100644 (file)
@@ -41,9 +41,11 @@ module Ganeti.HTools.Cluster
     , printSolution
     , printSolutionLine
     , formatCmds
-    , printNodes
     , involvedNodes
     , splitJobs
+    -- * Display functions
+    , printNodes
+    , printInsts
     -- * Balacing functions
     , checkMove
     , tryBalance
@@ -81,24 +83,24 @@ type AllocElement = (Node.List, Instance.Instance, [Node.Node])
 data Table = Table Node.List Instance.List Score [Placement]
              deriving (Show)
 
-data CStats = CStats { cs_fmem :: Int    -- ^ Cluster free mem
-                     , cs_fdsk :: Int    -- ^ Cluster free disk
-                     , cs_amem :: Int    -- ^ Cluster allocatable mem
-                     , cs_adsk :: Int    -- ^ Cluster allocatable disk
-                     , cs_acpu :: Int    -- ^ Cluster allocatable cpus
-                     , cs_mmem :: Int    -- ^ Max node allocatable mem
-                     , cs_mdsk :: Int    -- ^ Max node allocatable disk
-                     , cs_mcpu :: Int    -- ^ Max node allocatable cpu
-                     , cs_imem :: Int    -- ^ Instance used mem
-                     , cs_idsk :: Int    -- ^ Instance used disk
-                     , cs_icpu :: Int    -- ^ Instance used cpu
-                     , cs_tmem :: Double -- ^ Cluster total mem
-                     , cs_tdsk :: Double -- ^ Cluster total disk
-                     , cs_tcpu :: Double -- ^ Cluster total cpus
-                     , cs_xmem :: Int    -- ^ Unnacounted for mem
-                     , cs_nmem :: Int    -- ^ Node own memory
-                     , cs_score :: Score -- ^ The cluster score
-                     , cs_ninst :: Int   -- ^ The total number of instances
+data CStats = CStats { csFmem :: Int    -- ^ Cluster free mem
+                     , csFdsk :: Int    -- ^ Cluster free disk
+                     , csAmem :: Int    -- ^ Cluster allocatable mem
+                     , csAdsk :: Int    -- ^ Cluster allocatable disk
+                     , csAcpu :: Int    -- ^ Cluster allocatable cpus
+                     , csMmem :: Int    -- ^ Max node allocatable mem
+                     , csMdsk :: Int    -- ^ Max node allocatable disk
+                     , csMcpu :: Int    -- ^ Max node allocatable cpu
+                     , csImem :: Int    -- ^ Instance used mem
+                     , csIdsk :: Int    -- ^ Instance used disk
+                     , csIcpu :: Int    -- ^ Instance used cpu
+                     , csTmem :: Double -- ^ Cluster total mem
+                     , csTdsk :: Double -- ^ Cluster total disk
+                     , csTcpu :: Double -- ^ Cluster total cpus
+                     , csXmem :: Int    -- ^ Unnacounted for mem
+                     , csNmem :: Int    -- ^ Node own memory
+                     , csScore :: Score -- ^ The cluster score
+                     , csNinst :: Int   -- ^ The total number of instances
                      }
 
 -- * Utility functions
@@ -120,107 +122,126 @@ computeBadItems nl il =
   let bad_nodes = verifyN1 $ getOnline nl
       bad_instances = map (\idx -> Container.find idx il) .
                       sort . nub $
-                      concatMap (\ n -> Node.slist n ++ Node.plist n) bad_nodes
+                      concatMap (\ n -> Node.sList n ++ Node.pList n) bad_nodes
   in
     (bad_nodes, bad_instances)
 
 emptyCStats :: CStats
-emptyCStats = CStats { cs_fmem = 0
-                     , cs_fdsk = 0
-                     , cs_amem = 0
-                     , cs_adsk = 0
-                     , cs_acpu = 0
-                     , cs_mmem = 0
-                     , cs_mdsk = 0
-                     , cs_mcpu = 0
-                     , cs_imem = 0
-                     , cs_idsk = 0
-                     , cs_icpu = 0
-                     , cs_tmem = 0
-                     , cs_tdsk = 0
-                     , cs_tcpu = 0
-                     , cs_xmem = 0
-                     , cs_nmem = 0
-                     , cs_score = 0
-                     , cs_ninst = 0
+emptyCStats = CStats { csFmem = 0
+                     , csFdsk = 0
+                     , csAmem = 0
+                     , csAdsk = 0
+                     , csAcpu = 0
+                     , csMmem = 0
+                     , csMdsk = 0
+                     , csMcpu = 0
+                     , csImem = 0
+                     , csIdsk = 0
+                     , csIcpu = 0
+                     , csTmem = 0
+                     , csTdsk = 0
+                     , csTcpu = 0
+                     , csXmem = 0
+                     , csNmem = 0
+                     , csScore = 0
+                     , csNinst = 0
                      }
 
 updateCStats :: CStats -> Node.Node -> CStats
 updateCStats cs node =
-    let CStats { cs_fmem = x_fmem, cs_fdsk = x_fdsk,
-                 cs_amem = x_amem, cs_acpu = x_acpu, cs_adsk = x_adsk,
-                 cs_mmem = x_mmem, cs_mdsk = x_mdsk, cs_mcpu = x_mcpu,
-                 cs_imem = x_imem, cs_idsk = x_idsk, cs_icpu = x_icpu,
-                 cs_tmem = x_tmem, cs_tdsk = x_tdsk, cs_tcpu = x_tcpu,
-                 cs_xmem = x_xmem, cs_nmem = x_nmem, cs_ninst = x_ninst
+    let CStats { csFmem = x_fmem, csFdsk = x_fdsk,
+                 csAmem = x_amem, csAcpu = x_acpu, csAdsk = x_adsk,
+                 csMmem = x_mmem, csMdsk = x_mdsk, csMcpu = x_mcpu,
+                 csImem = x_imem, csIdsk = x_idsk, csIcpu = x_icpu,
+                 csTmem = x_tmem, csTdsk = x_tdsk, csTcpu = x_tcpu,
+                 csXmem = x_xmem, csNmem = x_nmem, csNinst = x_ninst
                }
             = cs
-        inc_amem = Node.f_mem node - Node.r_mem node
+        inc_amem = Node.fMem node - Node.rMem node
         inc_amem' = if inc_amem > 0 then inc_amem else 0
         inc_adsk = Node.availDisk node
-        inc_imem = truncate (Node.t_mem node) - Node.n_mem node
-                   - Node.x_mem node - Node.f_mem node
-        inc_icpu = Node.u_cpu node
-        inc_idsk = truncate (Node.t_dsk node) - Node.f_dsk node
-
-    in cs { cs_fmem = x_fmem + Node.f_mem node
-          , cs_fdsk = x_fdsk + Node.f_dsk node
-          , cs_amem = x_amem + inc_amem'
-          , cs_adsk = x_adsk + inc_adsk
-          , cs_acpu = x_acpu
-          , cs_mmem = max x_mmem inc_amem'
-          , cs_mdsk = max x_mdsk inc_adsk
-          , cs_mcpu = x_mcpu
-          , cs_imem = x_imem + inc_imem
-          , cs_idsk = x_idsk + inc_idsk
-          , cs_icpu = x_icpu + inc_icpu
-          , cs_tmem = x_tmem + Node.t_mem node
-          , cs_tdsk = x_tdsk + Node.t_dsk node
-          , cs_tcpu = x_tcpu + Node.t_cpu node
-          , cs_xmem = x_xmem + Node.x_mem node
-          , cs_nmem = x_nmem + Node.n_mem node
-          , cs_ninst = x_ninst + length (Node.plist node)
+        inc_imem = truncate (Node.tMem node) - Node.nMem node
+                   - Node.xMem node - Node.fMem node
+        inc_icpu = Node.uCpu node
+        inc_idsk = truncate (Node.tDsk node) - Node.fDsk node
+
+    in cs { csFmem = x_fmem + Node.fMem node
+          , csFdsk = x_fdsk + Node.fDsk node
+          , csAmem = x_amem + inc_amem'
+          , csAdsk = x_adsk + inc_adsk
+          , csAcpu = x_acpu
+          , csMmem = max x_mmem inc_amem'
+          , csMdsk = max x_mdsk inc_adsk
+          , csMcpu = x_mcpu
+          , csImem = x_imem + inc_imem
+          , csIdsk = x_idsk + inc_idsk
+          , csIcpu = x_icpu + inc_icpu
+          , csTmem = x_tmem + Node.tMem node
+          , csTdsk = x_tdsk + Node.tDsk node
+          , csTcpu = x_tcpu + Node.tCpu node
+          , csXmem = x_xmem + Node.xMem node
+          , csNmem = x_nmem + Node.nMem node
+          , csNinst = x_ninst + length (Node.pList node)
           }
 
 -- | Compute the total free disk and memory in the cluster.
 totalResources :: Node.List -> CStats
 totalResources nl =
     let cs = foldl' updateCStats emptyCStats . Container.elems $ nl
-    in cs { cs_score = compCV nl }
+    in cs { csScore = compCV nl }
+
+-- | The names of the individual elements in the CV list
+detailedCVNames :: [String]
+detailedCVNames = [ "free_mem_cv"
+                  , "free_disk_cv"
+                  , "n1_score"
+                  , "reserved_mem_cv"
+                  , "offline_score"
+                  , "vcpu_ratio_cv"
+                  , "cpu_load_cv"
+                  , "mem_load_cv"
+                  , "disk_load_cv"
+                  , "net_load_cv"
+                  ]
 
 -- | Compute the mem and disk covariance.
-compDetailedCV :: Node.List -> (Double, Double, Double, Double, Double, Double)
+compDetailedCV :: Node.List -> [Double]
 compDetailedCV nl =
     let
         all_nodes = Container.elems nl
         (offline, nodes) = partition Node.offline all_nodes
-        mem_l = map Node.p_mem nodes
-        dsk_l = map Node.p_dsk nodes
+        mem_l = map Node.pMem nodes
+        dsk_l = map Node.pDsk nodes
         mem_cv = varianceCoeff mem_l
         dsk_cv = varianceCoeff dsk_l
         n1_l = length $ filter Node.failN1 nodes
         n1_score = fromIntegral n1_l /
                    fromIntegral (length nodes)::Double
-        res_l = map Node.p_rem nodes
+        res_l = map Node.pRem nodes
         res_cv = varianceCoeff res_l
-        offline_inst = sum . map (\n -> (length . Node.plist $ n) +
-                                        (length . Node.slist $ n)) $ offline
-        online_inst = sum . map (\n -> (length . Node.plist $ n) +
-                                       (length . Node.slist $ n)) $ nodes
+        offline_inst = sum . map (\n -> (length . Node.pList $ n) +
+                                        (length . Node.sList $ n)) $ offline
+        online_inst = sum . map (\n -> (length . Node.pList $ n) +
+                                       (length . Node.sList $ n)) $ nodes
         off_score = if offline_inst == 0
                     then 0::Double
                     else fromIntegral offline_inst /
                          fromIntegral (offline_inst + online_inst)::Double
-        cpu_l = map Node.p_cpu nodes
+        cpu_l = map Node.pCpu nodes
         cpu_cv = varianceCoeff cpu_l
-    in (mem_cv, dsk_cv, n1_score, res_cv, off_score, cpu_cv)
+        (c_load, m_load, d_load, n_load) = unzip4 $
+            map (\n ->
+                     let DynUtil c1 m1 d1 n1 = Node.utilLoad n
+                         DynUtil c2 m2 d2 n2 = Node.utilPool n
+                     in (c1/c2, m1/m2, d1/d2, n1/n2)
+                ) nodes
+    in [ mem_cv, dsk_cv, n1_score, res_cv, off_score, cpu_cv
+       , varianceCoeff c_load, varianceCoeff m_load
+       , varianceCoeff d_load, varianceCoeff n_load]
 
 -- | Compute the /total/ variance.
 compCV :: Node.List -> Double
-compCV nl =
-    let (mem_cv, dsk_cv, n1_score, res_cv, off_score, cpu_cv) =
-            compDetailedCV nl
-    in mem_cv + dsk_cv + n1_score + res_cv + off_score + cpu_cv
+compCV = sum . compDetailedCV
 
 -- | Compute online nodes from a Node.List
 getOnline :: Node.List -> [Node.Node]
@@ -238,8 +259,8 @@ applyMove :: Node.List -> Instance.Instance
           -> IMove -> OpResult (Node.List, Instance.Instance, Ndx, Ndx)
 -- Failover (f)
 applyMove nl inst Failover =
-    let old_pdx = Instance.pnode inst
-        old_sdx = Instance.snode inst
+    let old_pdx = Instance.pNode inst
+        old_sdx = Instance.sNode inst
         old_p = Container.find old_pdx nl
         old_s = Container.find old_sdx nl
         int_p = Node.removePri old_p inst
@@ -254,8 +275,8 @@ applyMove nl inst Failover =
 
 -- Replace the primary (f:, r:np, f)
 applyMove nl inst (ReplacePrimary new_pdx) =
-    let old_pdx = Instance.pnode inst
-        old_sdx = Instance.snode inst
+    let old_pdx = Instance.pNode inst
+        old_sdx = Instance.sNode inst
         old_p = Container.find old_pdx nl
         old_s = Container.find old_sdx nl
         tgt_n = Container.find new_pdx nl
@@ -276,8 +297,8 @@ applyMove nl inst (ReplacePrimary new_pdx) =
 
 -- Replace the secondary (r:ns)
 applyMove nl inst (ReplaceSecondary new_sdx) =
-    let old_pdx = Instance.pnode inst
-        old_sdx = Instance.snode inst
+    let old_pdx = Instance.pNode inst
+        old_sdx = Instance.sNode inst
         old_s = Container.find old_sdx nl
         tgt_n = Container.find new_sdx nl
         int_s = Node.removeSec old_s inst
@@ -290,8 +311,8 @@ applyMove nl inst (ReplaceSecondary new_sdx) =
 
 -- Replace the secondary and failover (r:np, f)
 applyMove nl inst (ReplaceAndFailover new_pdx) =
-    let old_pdx = Instance.pnode inst
-        old_sdx = Instance.snode inst
+    let old_pdx = Instance.pNode inst
+        old_sdx = Instance.sNode inst
         old_p = Container.find old_pdx nl
         old_s = Container.find old_sdx nl
         tgt_n = Container.find new_pdx nl
@@ -308,8 +329,8 @@ applyMove nl inst (ReplaceAndFailover new_pdx) =
 
 -- Failver and replace the secondary (f, r:ns)
 applyMove nl inst (FailoverAndReplace new_sdx) =
-    let old_pdx = Instance.pnode inst
-        old_sdx = Instance.snode inst
+    let old_pdx = Instance.pNode inst
+        old_sdx = Instance.sNode inst
         old_p = Container.find old_pdx nl
         old_s = Container.find old_sdx nl
         tgt_n = Container.find new_sdx nl
@@ -395,8 +416,8 @@ checkInstanceMove :: [Ndx]             -- ^ Allowed target node indices
                   -> Table             -- ^ Best new table for this instance
 checkInstanceMove nodes_idx disk_moves ini_tbl target =
     let
-        opdx = Instance.pnode target
-        osdx = Instance.snode target
+        opdx = Instance.pNode target
+        osdx = Instance.sNode target
         nodes = filter (\idx -> idx /= opdx && idx /= osdx) nodes_idx
         use_secondary = elem osdx nodes_idx
         aft_failover = if use_secondary -- if allowed to failover
@@ -421,7 +442,7 @@ checkMove nodes_idx disk_moves ini_tbl victims =
         best_tbl =
             foldl'
             (\ step_tbl em ->
-                 if Instance.snode em == Node.noSecondary then step_tbl
+                 if Instance.sNode em == Node.noSecondary then step_tbl
                     else compareTables step_tbl $
                          checkInstanceMove nodes_idx disk_moves ini_tbl em)
             ini_tbl victims
@@ -524,7 +545,7 @@ tryReloc :: (Monad m) =>
 tryReloc nl il xid 1 ex_idx =
     let all_nodes = getOnline nl
         inst = Container.find xid il
-        ex_idx' = Instance.pnode inst:ex_idx
+        ex_idx' = Instance.pNode inst:ex_idx
         valid_nodes = filter (not . flip elem ex_idx' . Node.idx) all_nodes
         valid_idxes = map Node.idx valid_nodes
         sols1 = foldl' (\cstate x ->
@@ -596,8 +617,8 @@ printSolutionLine nl il nmlen imlen plc pos =
         inam = Instance.name inst
         npri = Container.nameOf nl p
         nsec = Container.nameOf nl s
-        opri = Container.nameOf nl $ Instance.pnode inst
-        osec = Container.nameOf nl $ Instance.snode inst
+        opri = Container.nameOf nl $ Instance.pNode inst
+        osec = Container.nameOf nl $ Instance.sNode inst
         (moves, cmds) =  computeMoves inst inam opri osec npri nsec
         ostr = printf "%s:%s" opri osec::String
         nstr = printf "%s:%s" npri nsec::String
@@ -612,8 +633,8 @@ involvedNodes :: Instance.List -> Placement -> [Ndx]
 involvedNodes il plc =
     let (i, np, ns, _, _) = plc
         inst = Container.find i il
-        op = Instance.pnode inst
-        os = Instance.snode inst
+        op = Instance.pNode inst
+        os = Instance.sNode inst
     in nub [np, ns, op, os]
 
 -- | Inner function for splitJobs, that either appends the next job to
@@ -666,33 +687,49 @@ printSolution nl il sol =
 printNodes :: Node.List -> String
 printNodes nl =
     let snl = sortBy (compare `on` Node.idx) (Container.elems nl)
-        m_name = maximum . map (length . Node.name) $ snl
-        helper = Node.list m_name
-        header = printf
-                 "%2s %-*s %5s %5s %5s %5s %5s %5s %5s %5s %4s %4s \
-                 \%3s %3s %6s %6s %5s"
-                 " F" m_name "Name"
-                 "t_mem" "n_mem" "i_mem" "x_mem" "f_mem" "r_mem"
-                 "t_dsk" "f_dsk" "pcpu" "vcpu"
-                 "pri" "sec" "p_fmem" "p_fdsk" "r_cpu"::String
-    in unlines (header:map helper snl)
+        header = ["F", "Name"
+                 , "t_mem", "n_mem", "i_mem", "x_mem", "f_mem", "r_mem"
+                 , "t_dsk", "f_dsk", "pcpu", "vcpu", "pri",  "sec"
+                 , "p_fmem", "p_fdsk", "r_cpu"
+                 , "lCpu", "lMem", "lDsk", "lNet" ]
+        isnum = False:False:repeat True
+    in unlines . map ((:) ' ' .  intercalate " ") $
+       formatTable (header:map Node.list snl) isnum
+
+-- | Print the instance list.
+printInsts :: Node.List -> Instance.List -> String
+printInsts nl il =
+    let sil = sortBy (compare `on` Instance.idx) (Container.elems il)
+        helper inst = [ (Instance.name inst)
+                      , (Container.nameOf nl (Instance.pNode inst))
+                      , (let sdx = Instance.sNode inst
+                         in if sdx == Node.noSecondary
+                            then  ""
+                            else Container.nameOf nl sdx) ]
+        header = ["Name", "Pri_node", "Sec_node"]
+        isnum = repeat False
+    in unlines . map ((:) ' ' . intercalate " ") $
+       formatTable (header:map helper sil) isnum
 
 -- | Shows statistics for a given node list.
 printStats :: Node.List -> String
 printStats nl =
-    let (mem_cv, dsk_cv, n1_score, res_cv, off_score, cpu_cv) =
-            compDetailedCV nl
-    in printf "f_mem=%.8f, r_mem=%.8f, f_dsk=%.8f, n1=%.3f, \
-              \uf=%.3f, r_cpu=%.3f"
-       mem_cv res_cv dsk_cv n1_score off_score cpu_cv
+    let dcvs = compDetailedCV nl
+        hd = zip (detailedCVNames ++ repeat "unknown") dcvs
+        formatted = map (\(header, val) ->
+                             printf "%s=%.8f" header val::String) hd
+    in intercalate ", " formatted
 
 -- | Convert a placement into a list of OpCodes (basically a job).
 iMoveToJob :: String -> Node.List -> Instance.List
           -> Idx -> IMove -> [OpCodes.OpCode]
 iMoveToJob csf nl il idx move =
-    let iname = Container.nameOf il idx ++ csf
+    let inst = Container.find idx il
+        iname = Instance.name inst ++ csf
         lookNode n = Just (Container.nameOf nl n ++ csf)
-        opF = OpCodes.OpFailoverInstance iname False
+        opF = if Instance.running inst
+              then OpCodes.OpMigrateInstance iname True False
+              else OpCodes.OpFailoverInstance iname False
         opR n = OpCodes.OpReplaceDisks iname (lookNode n)
                 OpCodes.ReplaceNewSecondary [] Nothing
     in case move of