Revision efe98965

b/htools/Ganeti/HTools/Loader.hs
40 40
    , Request(..)
41 41
    , ClusterData(..)
42 42
    , emptyCluster
43
    , compareNameComponent
44
    , prefixMatch
45
    , LookupResult(..)
46
    , MatchPriority(..)
43 47
    ) where
44 48

  
45 49
import Data.List
50
import Data.Function
46 51
import qualified Data.Map as M
47 52
import Text.Printf (printf)
48 53

  
......
52 57
import qualified Ganeti.HTools.Group as Group
53 58

  
54 59
import Ganeti.HTools.Types
60
import Ganeti.HTools.Utils
55 61

  
56 62
-- * Constants
57 63

  
......
94 100
    , cdTags      :: [String]      -- ^ The cluster tags
95 101
    } deriving (Show, Read)
96 102

  
103
-- | The priority of a match in a lookup result.
104
data MatchPriority = ExactMatch
105
                   | MultipleMatch
106
                   | PartialMatch
107
                   | FailMatch
108
                   deriving (Show, Read, Enum, Eq, Ord)
109

  
110
-- | The result of a name lookup in a list.
111
data LookupResult = LookupResult
112
    { lrMatchPriority :: MatchPriority -- ^ The result type
113
    -- | Matching value (for ExactMatch, PartialMatch), Lookup string otherwise
114
    , lrContent :: String
115
    } deriving (Show, Read)
116

  
117
-- | Lookup results have an absolute preference ordering.
118
instance Eq LookupResult where
119
  (==) = (==) `on` lrMatchPriority
120

  
121
instance Ord LookupResult where
122
  compare = compare `on` lrMatchPriority
123

  
97 124
-- | An empty cluster.
98 125
emptyCluster :: ClusterData
99 126
emptyCluster = ClusterData Container.empty Container.empty Container.empty []
......
121 148
      Nothing -> fail $ "Unknown group '" ++ gname ++ "' for node " ++ nname
122 149
      Just idx -> return idx
123 150

  
151
-- | Check for prefix matches in names.
152
-- Implemented in Ganeti core utils.text.MatchNameComponent
153
-- as the regexp r"^%s(\..*)?$" % re.escape(key)
154
prefixMatch :: String  -- ^ Lookup
155
            -> String  -- ^ Full name
156
            -> Bool    -- ^ Whether there is a prefix match
157
prefixMatch lkp = isPrefixOf (lkp ++ ".")
158

  
159
-- | Is the lookup priority a "good" one?
160
goodMatchPriority :: MatchPriority -> Bool
161
goodMatchPriority ExactMatch = True
162
goodMatchPriority PartialMatch = True
163
goodMatchPriority _ = False
164

  
165
-- | Is the lookup result an actual match?
166
goodLookupResult :: LookupResult -> Bool
167
goodLookupResult = goodMatchPriority . lrMatchPriority
168

  
169
-- | Compares a canonical name and a lookup string.
170
compareNameComponent :: String        -- ^ Canonical (target) name
171
                     -> String        -- ^ Partial (lookup) name
172
                     -> LookupResult  -- ^ Result of the lookup
173
compareNameComponent cnl lkp =
174
  select (LookupResult FailMatch lkp)
175
  [ (cnl == lkp          , LookupResult ExactMatch cnl)
176
  , (prefixMatch lkp cnl , LookupResult PartialMatch cnl)
177
  ]
178

  
179
-- | Lookup a string and choose the best result.
180
chooseLookupResult :: String       -- ^ Lookup key
181
                   -> String       -- ^ String to compare to the lookup key
182
                   -> LookupResult -- ^ Previous result
183
                   -> LookupResult -- ^ New result
184
chooseLookupResult lkp cstr old =
185
  -- default: use class order to pick the minimum result
186
  select (min new old)
187
  -- special cases:
188
  -- short circuit if the new result is an exact match
189
  [ ((lrMatchPriority new) == ExactMatch, new)
190
  -- if both are partial matches generate a multiple match
191
  , (partial2, LookupResult MultipleMatch lkp)
192
  ] where new = compareNameComponent cstr lkp
193
          partial2 = all ((PartialMatch==) . lrMatchPriority) [old, new]
194

  
195
-- | Find the canonical name for a lookup string in a list of names.
196
lookupName :: [String]      -- ^ List of keys
197
           -> String        -- ^ Lookup string
198
           -> LookupResult  -- ^ Result of the lookup
199
lookupName l s = foldr (chooseLookupResult s)
200
                       (LookupResult FailMatch s) l
201

  
124 202
-- | Given a list of elements (and their names), assign indices to them.
125 203
assignIndices :: (Element a) =>
126 204
                 [(String, a)]
b/htools/Ganeti/HTools/QC.hs
992 992
      in (sum . map (length . Node.pList)) nodes == 0 &&
993 993
         null instances
994 994

  
995
-- | Check that compareNameComponent on equal strings works.
996
prop_Loader_compareNameComponent_equal :: String -> Bool
997
prop_Loader_compareNameComponent_equal s =
998
  Loader.compareNameComponent s s ==
999
    Loader.LookupResult Loader.ExactMatch s
1000

  
1001
-- | Check that compareNameComponent on prefix strings works.
1002
prop_Loader_compareNameComponent_prefix :: NonEmptyList Char -> String -> Bool
1003
prop_Loader_compareNameComponent_prefix (NonEmpty s1) s2 =
1004
  Loader.compareNameComponent (s1 ++ "." ++ s2) s1 ==
1005
    Loader.LookupResult Loader.PartialMatch s1
1006

  
995 1007
testLoader =
996 1008
  [ run prop_Loader_lookupNode
997 1009
  , run prop_Loader_lookupInstance
998 1010
  , run prop_Loader_assignIndices
999 1011
  , run prop_Loader_mergeData
1012
  , run prop_Loader_compareNameComponent_equal
1013
  , run prop_Loader_compareNameComponent_prefix
1000 1014
  ]
1001 1015

  
1002 1016
-- ** Types tests

Also available in: Unified diff