Revision 040afc35 Ganeti/HTools/Cluster.hs

b/Ganeti/HTools/Cluster.hs
85 85
data Table = Table NodeList InstanceList Score [Placement]
86 86
             deriving (Show)
87 87

  
88
-- | Constant node index for a non-moveable instance
89
noSecondary :: Int
90
noSecondary = -1
91

  
92 88
-- General functions
93 89

  
94 90
-- | Cap the removal list if needed.
......
480 476
        best_tbl =
481 477
            foldl'
482 478
            (\ step_tbl elem ->
483
                 if Instance.snode elem == noSecondary then step_tbl
479
                 if Instance.snode elem == Node.noSecondary then step_tbl
484 480
                    else compareTables step_tbl $
485 481
                         checkInstanceMove nodes_idx ini_tbl elem)
486 482
            ini_tbl victims
......
694 690

  
695 691
-- Loading functions
696 692

  
697
{- | Convert newline and delimiter-separated text.
698

  
699
This function converts a text in tabular format as generated by
700
@gnt-instance list@ and @gnt-node list@ to a list of objects using a
701
supplied conversion function.
702

  
703
-}
704
loadTabular :: (Monad m) => String -> ([String] -> m (String, a))
705
            -> (a -> Int -> a) -> m ([(String, Int)], [(Int, a)])
706
loadTabular text_data convert_fn set_fn = do
707
  let lines_data = lines text_data
708
      rows = map (sepSplit '|') lines_data
709
  kerows <- mapM convert_fn rows
710
  let idxrows = map (\ (idx, (k, v)) -> ((k, idx), (idx, set_fn v idx)))
711
                (zip [0..] kerows)
712
  return $ unzip idxrows
713

  
714 693
-- | For each instance, add its index to its primary and secondary nodes
715 694
fixNodes :: [(Int, Node.Node)]
716 695
         -> [(Int, Instance.Instance)]
......
726 705
                    ac1 = deleteBy assocEqual (pdx, pold) accu
727 706
                    ac2 = (pdx, pnew):ac1
728 707
                in
729
                  if sdx /= noSecondary then
708
                  if sdx /= Node.noSecondary then
730 709
                      let
731 710
                          sold = fromJust $ lookup sdx accu
732 711
                          snew = Node.setSec sold idx
......
756 735
    let sflen = length suffix in
757 736
    map (\ (key, name) -> (key, take ((length name) - sflen) name)) lst
758 737

  
759
-- | Safe 'read' function returning data encapsulated in a Result
760
tryRead :: (Monad m, Read a) => String -> String -> m a
761
tryRead name s =
762
    let sols = readsPrec 0 s
763
    in case sols of
764
         (v, ""):[] -> return v
765
         (_, e):[] -> fail $ name ++ ": leftover characters when parsing '"
766
                      ++ s ++ "': '" ++ e ++ "'"
767
         _ -> fail $ name ++ ": cannot parse string '" ++ s ++ "'"
768

  
769
-- | Lookups a node into an assoc list
770
lookupNode :: (Monad m) => String -> String -> [(String, Int)] -> m Int
771
lookupNode node inst ktn =
772
    case lookup node ktn of
773
      Nothing -> fail $ "Unknown node '" ++ node ++ "' for instance " ++ inst
774
      Just idx -> return idx
775

  
776
-- | Load a node from a field list
777
loadNode :: (Monad m) => [String] -> m (String, Node.Node)
778
loadNode (name:tm:nm:fm:td:fd:fo:[]) = do
779
  new_node <-
780
      if any (== "?") [tm,nm,fm,td,fd] || fo == "Y" then
781
          return $ Node.create 0 0 0 0 0 True
782
      else do
783
        vtm <- tryRead name tm
784
        vnm <- tryRead name nm
785
        vfm <- tryRead name fm
786
        vtd <- tryRead name td
787
        vfd <- tryRead name fd
788
        return $ Node.create vtm vnm vfm vtd vfd False
789
  return (name, new_node)
790
loadNode s = fail $ "Invalid/incomplete node data: '" ++ (show s) ++ "'"
791

  
792
-- | Load an instance from a field list
793
loadInst :: (Monad m) =>
794
            [(String, Int)] -> [String] -> m (String, Instance.Instance)
795
loadInst ktn (name:mem:dsk:status:pnode:snode:[]) = do
796
  pidx <- lookupNode pnode name ktn
797
  sidx <- (if null snode then return noSecondary
798
           else lookupNode snode name ktn)
799
  vmem <- tryRead name mem
800
  vdsk <- tryRead name dsk
801
  when (sidx == pidx) $ fail $ "Instance " ++ name ++
802
           " has same primary and secondary node - " ++ pnode
803
  let newinst = Instance.create vmem vdsk status pidx sidx
804
  return (name, newinst)
805
loadInst _ s = fail $ "Invalid/incomplete instance data: '" ++ (show s) ++ "'"
806 738

  
807 739
{-| Initializer function that loads the data from a node and list file
808 740
    and massages it into the correct format. -}
809
loadData :: String -- ^ Node data in text format
810
         -> String -- ^ Instance data in text format
811
         -> Result (Container.Container Node.Node,
812
                    Container.Container Instance.Instance,
813
                    String, NameList, NameList)
814
loadData ndata idata = do
815
  {- node file: name t_mem n_mem f_mem t_disk f_disk -}
816
  (ktn, nl) <- loadTabular ndata loadNode Node.setIdx
817
      {- instance file: name mem disk status pnode snode -}
818
  (kti, il) <- loadTabular idata (loadInst ktn) Instance.setIdx
741
loadData :: ([(String, Int)], Node.AssocList,
742
             [(String, Int)], Instance.AssocList) -- ^ Data from either
743
                                                  -- Text.loadData
744
                                                  -- or Rapi.loadData
745
         -> Result (NodeList, InstanceList, String, NameList, NameList)
746
loadData (ktn, nl, kti, il) = do
819 747
  let
820 748
      nl2 = fixNodes nl il
821 749
      il3 = Container.fromAssocList il

Also available in: Unified diff