From ad1c1e41d9b9fa41cfcab7a192e33ada023f18ff Mon Sep 17 00:00:00 2001 From: Iustin Pop <iustin@google.com> Date: Mon, 10 Dec 2012 09:32:13 +0100 Subject: [PATCH] Implement opcode summary support This implements in the Haskell codebase the opcode summary. As opposed to Python, we always use custom code for formatting, since we don't want to use dynamic attribute lookup. To test this properly, we need to change MetaOpCode to record-syntax, so that we get accessor functions. Signed-off-by: Iustin Pop <iustin@google.com> Reviewed-by: Helga Velroyen <helgav@google.com> --- htest/Test/Ganeti/OpCodes.hs | 12 ++++-- htools/Ganeti/OpCodes.hs | 74 +++++++++++++++++++++++++++++++-- htools/Ganeti/OpParams.hs | 1 + htools/Ganeti/Query/Language.hs | 1 + 4 files changed, 81 insertions(+), 7 deletions(-) diff --git a/htest/Test/Ganeti/OpCodes.hs b/htest/Test/Ganeti/OpCodes.hs index b9398ecc1..e3ed1f397 100644 --- a/htest/Test/Ganeti/OpCodes.hs +++ b/htest/Test/Ganeti/OpCodes.hs @@ -451,6 +451,8 @@ case_py_compat_types = do sample_opcodes <- sample' (vectorOf num_opcodes (arbitrary::Gen OpCodes.MetaOpCode)) let opcodes = head sample_opcodes + with_sum = map (\o -> (OpCodes.opSummary $ + OpCodes.metaOpCode o, o)) opcodes serialized = J.encode opcodes -- check for non-ASCII fields, usually due to 'arbitrary :: String' mapM_ (\op -> when (any (not . isAscii) (J.encode op)) . @@ -465,10 +467,12 @@ case_py_compat_types = do \decoded = [opcodes.OpCode.LoadOpCode(o) for o in op_data]\n\ \for op in decoded:\n\ \ op.Validate(True)\n\ - \encoded = [op.__getstate__() for op in decoded]\n\ + \encoded = [(op.Summary(), op.__getstate__())\n\ + \ for op in decoded]\n\ \print serializer.Dump(encoded)" serialized >>= checkPythonResult - let deserialised = J.decode py_stdout::J.Result [OpCodes.MetaOpCode] + let deserialised = + J.decode py_stdout::J.Result [(String, OpCodes.MetaOpCode)] decoded <- case deserialised of J.Ok ops -> return ops J.Error msg -> @@ -477,9 +481,9 @@ case_py_compat_types = do -- for proper types >> fail "Unable to decode opcodes" HUnit.assertEqual "Mismatch in number of returned opcodes" - (length opcodes) (length decoded) + (length decoded) (length with_sum) mapM_ (uncurry (HUnit.assertEqual "Different result after encoding/decoding") - ) $ zip opcodes decoded + ) $ zip decoded with_sum -- | Custom HUnit test case that forks a Python process and checks -- correspondence between Haskell OpCodes fields and their Python diff --git a/htools/Ganeti/OpCodes.hs b/htools/Ganeti/OpCodes.hs index 1e8f56126..f0ffd00f3 100644 --- a/htools/Ganeti/OpCodes.hs +++ b/htools/Ganeti/OpCodes.hs @@ -38,6 +38,7 @@ module Ganeti.OpCodes , opID , allOpIDs , allOpFields + , opSummary , CommonOpParams(..) , defOpParams , MetaOpCode(..) @@ -45,13 +46,15 @@ module Ganeti.OpCodes , setOpComment ) where +import Data.Maybe (fromMaybe) import Text.JSON (readJSON, showJSON, JSON, JSValue, makeObj) import qualified Text.JSON import Ganeti.THH import Ganeti.OpParams -import Ganeti.Types (OpSubmitPriority(..)) +import Ganeti.Types (OpSubmitPriority(..), fromNonEmpty) +import Ganeti.Query.Language (queryTypeOpToRaw) -- | OpCode representation. -- @@ -548,6 +551,70 @@ instance JSON OpCode where readJSON = loadOpCode showJSON = saveOpCode +-- | Generates the summary value for an opcode. +opSummaryVal :: OpCode -> Maybe String +opSummaryVal OpClusterVerifyGroup { opGroupName = s } = Just (fromNonEmpty s) +opSummaryVal OpGroupVerifyDisks { opGroupName = s } = Just (fromNonEmpty s) +opSummaryVal OpClusterRename { opName = s } = Just (fromNonEmpty s) +opSummaryVal OpQuery { opWhat = s } = Just (queryTypeOpToRaw s) +opSummaryVal OpQueryFields { opWhat = s } = Just (queryTypeOpToRaw s) +opSummaryVal OpNodeRemove { opNodeName = s } = Just (fromNonEmpty s) +opSummaryVal OpNodeAdd { opNodeName = s } = Just (fromNonEmpty s) +opSummaryVal OpNodeModifyStorage { opNodeName = s } = Just (fromNonEmpty s) +opSummaryVal OpRepairNodeStorage { opNodeName = s } = Just (fromNonEmpty s) +opSummaryVal OpNodeSetParams { opNodeName = s } = Just (fromNonEmpty s) +opSummaryVal OpNodePowercycle { opNodeName = s } = Just (fromNonEmpty s) +opSummaryVal OpNodeMigrate { opNodeName = s } = Just (fromNonEmpty s) +opSummaryVal OpNodeEvacuate { opNodeName = s } = Just (fromNonEmpty s) +opSummaryVal OpInstanceCreate { opInstanceName = s } = Just s +opSummaryVal OpInstanceReinstall { opInstanceName = s } = Just s +opSummaryVal OpInstanceRemove { opInstanceName = s } = Just s +-- FIXME: instance rename should show both names; currently it shows none +-- opSummaryVal OpInstanceRename { opInstanceName = s } = Just s +opSummaryVal OpInstanceStartup { opInstanceName = s } = Just s +opSummaryVal OpInstanceShutdown { opInstanceName = s } = Just s +opSummaryVal OpInstanceReboot { opInstanceName = s } = Just s +opSummaryVal OpInstanceReplaceDisks { opInstanceName = s } = Just s +opSummaryVal OpInstanceFailover { opInstanceName = s } = Just s +opSummaryVal OpInstanceMigrate { opInstanceName = s } = Just s +opSummaryVal OpInstanceMove { opInstanceName = s } = Just s +opSummaryVal OpInstanceConsole { opInstanceName = s } = Just s +opSummaryVal OpInstanceActivateDisks { opInstanceName = s } = Just s +opSummaryVal OpInstanceDeactivateDisks { opInstanceName = s } = Just s +opSummaryVal OpInstanceRecreateDisks { opInstanceName = s } = Just s +opSummaryVal OpInstanceSetParams { opInstanceName = s } = Just s +opSummaryVal OpInstanceGrowDisk { opInstanceName = s } = Just s +opSummaryVal OpInstanceChangeGroup { opInstanceName = s } = Just s +opSummaryVal OpGroupAdd { opGroupName = s } = Just (fromNonEmpty s) +opSummaryVal OpGroupAssignNodes { opGroupName = s } = Just (fromNonEmpty s) +opSummaryVal OpGroupSetParams { opGroupName = s } = Just (fromNonEmpty s) +opSummaryVal OpGroupRemove { opGroupName = s } = Just (fromNonEmpty s) +opSummaryVal OpGroupEvacuate { opGroupName = s } = Just (fromNonEmpty s) +opSummaryVal OpBackupPrepare { opInstanceName = s } = Just s +opSummaryVal OpBackupExport { opInstanceName = s } = Just s +opSummaryVal OpBackupRemove { opInstanceName = s } = Just s +opSummaryVal OpTagsGet { opKind = k } = + Just . fromMaybe "None" $ tagNameOf k +opSummaryVal OpTagsSearch { opTagSearchPattern = s } = Just (fromNonEmpty s) +opSummaryVal OpTestDelay { opDelayDuration = d } = Just (show d) +opSummaryVal OpTestAllocator { opIallocator = s } = + -- FIXME: Python doesn't handle None fields well, so we have behave the same + Just $ maybe "None" fromNonEmpty s +opSummaryVal OpNetworkAdd { opNetworkName = s} = Just (fromNonEmpty s) +opSummaryVal OpNetworkRemove { opNetworkName = s} = Just (fromNonEmpty s) +opSummaryVal OpNetworkSetParams { opNetworkName = s} = Just (fromNonEmpty s) +opSummaryVal OpNetworkConnect { opNetworkName = s} = Just (fromNonEmpty s) +opSummaryVal OpNetworkDisconnect { opNetworkName = s} = Just (fromNonEmpty s) +opSummaryVal _ = Nothing + +-- | Computes the summary of the opcode. +opSummary :: OpCode -> String +opSummary op = + case opSummaryVal op of + Nothing -> op_suffix + Just s -> op_suffix ++ "(" ++ s ++ ")" + where op_suffix = drop 3 $ opID op + -- | Generic\/common opcode parameters. $(buildObject "CommonOpParams" "op" [ pDryRun @@ -568,8 +635,9 @@ defOpParams = } -- | The top-level opcode type. -data MetaOpCode = MetaOpCode CommonOpParams OpCode - deriving (Show, Eq) +data MetaOpCode = MetaOpCode { metaParams :: CommonOpParams + , metaOpCode :: OpCode + } deriving (Show, Eq) -- | JSON serialisation for 'MetaOpCode'. showMeta :: MetaOpCode -> JSValue diff --git a/htools/Ganeti/OpParams.hs b/htools/Ganeti/OpParams.hs index e375ae596..acb47ba60 100644 --- a/htools/Ganeti/OpParams.hs +++ b/htools/Ganeti/OpParams.hs @@ -35,6 +35,7 @@ module Ganeti.OpParams ( TagType(..) , TagObject(..) , tagObjectFrom + , tagNameOf , decodeTagObject , encodeTagObject , ReplaceDisksMode(..) diff --git a/htools/Ganeti/Query/Language.hs b/htools/Ganeti/Query/Language.hs index c54f0f5a3..f07443998 100644 --- a/htools/Ganeti/Query/Language.hs +++ b/htools/Ganeti/Query/Language.hs @@ -48,6 +48,7 @@ module Ganeti.Query.Language , ResultValue , ItemType(..) , QueryTypeOp(..) + , queryTypeOpToRaw , QueryTypeLuxi(..) , checkRS ) where -- GitLab