Commit dae1f9cb authored by Guido Trotter's avatar Guido Trotter
Browse files

HTools/Node: add mkNodeGraph function



This function helps treating node node problems as graph problems.
It can transform a list of nodes plus a list of instances into a graph
which uses the nodes as vertices, and instances as edges connecting them
(as long as they have both a primary and a secondary node)
Signed-off-by: default avatarGuido Trotter <ultrotter@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent 34a21cc4
......@@ -36,15 +36,19 @@ module Test.Ganeti.HTools.Node
) where
import Test.QuickCheck
import Test.HUnit
import Control.Monad
import qualified Data.Map as Map
import qualified Data.Graph as Graph
import Data.List
import Test.Ganeti.TestHelper
import Test.Ganeti.TestCommon
import Test.Ganeti.TestHTools
import Test.Ganeti.HTools.Instance (genInstanceSmallerThanNode)
import Test.Ganeti.HTools.Instance ( genInstanceSmallerThanNode
, genInstanceList
, genInstanceOnNodeList)
import Ganeti.BasicTypes
import qualified Ganeti.HTools.Loader as Loader
......@@ -52,6 +56,9 @@ import qualified Ganeti.HTools.Container as Container
import qualified Ganeti.HTools.Instance as Instance
import qualified Ganeti.HTools.Node as Node
import qualified Ganeti.HTools.Types as Types
import qualified Ganeti.HTools.Graph as HGraph
{-# ANN module "HLint: ignore Use camelCase" #-}
-- * Arbitrary instances
......@@ -107,6 +114,14 @@ genNodeList :: Gen Node.Node -> Gen Node.List
genNodeList ngen = fmap (snd . Loader.assignIndices) names_nodes
where names_nodes = (fmap . map) (\n -> (Node.name n, n)) $ listOf1 ngen
-- | Generate a node list, an instance list, and a node graph.
-- We choose instances with nodes contained in the node list.
genNodeGraph :: Gen (Maybe Graph.Graph, Node.List, Instance.List)
genNodeGraph = do
nl <- genNodeList genOnlineNode `suchThat` ((2<=).Container.size)
il <- genInstanceList (genInstanceOnNodeList nl)
return (Node.mkNodeGraph nl il, nl, il)
-- * Test cases
prop_setAlias :: Node.Node -> String -> Bool
......@@ -316,6 +331,56 @@ prop_addSec_idempotent =
Ok node' -> Node.removeSec node' inst'' ==? node
_ -> failTest "Can't add instance"
-- | Check that no graph is created on an empty node list.
case_emptyNodeList :: Assertion
case_emptyNodeList =
assertEqual "" Nothing $ Node.mkNodeGraph emptynodes emptyinstances
where emptynodes = Container.empty :: Node.List
emptyinstances = Container.empty :: Instance.List
-- | Check that the number of vertices of a nodegraph is equal to the number of
-- nodes in the original node list.
prop_numVertices :: Property
prop_numVertices =
forAll genNodeGraph $ \(graph, nl, _) ->
(fmap numvertices graph ==? Just (Container.size nl))
where numvertices = length . Graph.vertices
-- | Check that the number of edges of a nodegraph is equal to twice the number
-- of instances with secondary nodes in the original instance list.
prop_numEdges :: Property
prop_numEdges =
forAll genNodeGraph $ \(graph, _, il) ->
(fmap numedges graph ==? Just (numwithsec il * 2))
where numedges = length . Graph.edges
numwithsec = length . filter Instance.hasSecondary . Container.elems
-- | Check that a node graph is colorable.
prop_nodeGraphIsColorable :: Property
prop_nodeGraphIsColorable =
forAll genNodeGraph $ \(graph, _, _) ->
fmap HGraph.isColorable graph ==? Just True
-- | Check that each edge in a nodegraph is an instance.
prop_instanceIsEdge :: Property
prop_instanceIsEdge =
forAll genNodeGraph $ \(graph, _, il) ->
fmap (\g -> all (`isEdgeOn` g) (iwithsec il)) graph ==? Just True
where i `isEdgeOn` g = iEdges i `intersect` Graph.edges g == iEdges i
iEdges i = [ (Instance.pNode i, Instance.sNode i)
, (Instance.sNode i, Instance.pNode i)]
iwithsec = filter Instance.hasSecondary . Container.elems
-- | Check that each instance in an edge in the resulting nodegraph.
prop_edgeIsInstance :: Property
prop_edgeIsInstance =
forAll genNodeGraph $ \(graph, _, il) ->
fmap (all (`isInstanceIn` il).Graph.edges) graph ==? Just True
where e `isInstanceIn` il = any (`hasNodes` e) (Container.elems il)
i `hasNodes` (v1,v2) =
Instance.allNodes i `elem` permutations [v1,v2]
-- | List of tests for the Node module.
testSuite "HTools/Node"
[ 'prop_setAlias
, 'prop_setOffline
......@@ -338,4 +403,10 @@ testSuite "HTools/Node"
, 'prop_computeGroups
, 'prop_addPri_idempotent
, 'prop_addSec_idempotent
, 'case_emptyNodeList
, 'prop_numVertices
, 'prop_numEdges
, 'prop_nodeGraphIsColorable
, 'prop_edgeIsInstance
, 'prop_instanceIsEdge
]
......@@ -70,13 +70,17 @@ module Ganeti.HTools.Node
, AllocElement
, noSecondary
, computeGroups
, mkNodeGraph
) where
import Data.List hiding (group)
import qualified Data.Map as Map
import qualified Data.Foldable as Foldable
import qualified Data.IntMap as IntMap
import qualified Data.Graph as Graph
import Data.Ord (comparing)
import Text.Printf (printf)
import Control.Monad (liftM, liftM2)
import qualified Ganeti.HTools.Container as Container
import qualified Ganeti.HTools.Instance as Instance
......@@ -544,6 +548,38 @@ availCpu t =
iMem :: Node -> Int
iMem t = truncate (tMem t) - nMem t - xMem t - fMem t
-- * Node graph functions
-- These functions do the transformations needed so that nodes can be
-- represented as a graph connected by the instances that are replicated
-- on them.
-- * Making of a Graph from a node/instance list
-- | Transform an instance into a list of edges on the node graph
instanceToEdges :: Instance.Instance -> [Graph.Edge]
instanceToEdges i
| Instance.hasSecondary i = [(pnode,snode), (snode,pnode)]
| otherwise = []
where pnode = Instance.pNode i
snode = Instance.sNode i
-- | Transform the list of instances into list of destination edges
instancesToEdges :: Instance.List -> [Graph.Edge]
instancesToEdges = concatMap instanceToEdges . Container.elems
-- | Transform the list of nodes into vertices bounds.
-- Returns Nothing is the list is empty.
nodesToBounds :: List -> Maybe Graph.Bounds
nodesToBounds nl = liftM2 (,) nmin nmax
where nmin = fmap (fst . fst) (IntMap.minViewWithKey nl)
nmax = fmap (fst . fst) (IntMap.maxViewWithKey nl)
-- | Transform a Node + Instance list into a NodeGraph type.
-- Returns Nothing if the node list is empty.
mkNodeGraph :: List -> Instance.List -> Maybe Graph.Graph
mkNodeGraph nl il =
liftM (`Graph.buildG` instancesToEdges il) (nodesToBounds nl)
-- * Display functions
-- | Return a field for a given node.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment