Commit ad1c1e41 authored by Iustin Pop's avatar Iustin Pop
Browse files

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: default avatarIustin Pop <iustin@google.com>
Reviewed-by: default avatarHelga Velroyen <helgav@google.com>
parent 8bc17ebb
......@@ -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
......
......@@ -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
......
......@@ -35,6 +35,7 @@ module Ganeti.OpParams
( TagType(..)
, TagObject(..)
, tagObjectFrom
, tagNameOf
, decodeTagObject
, encodeTagObject
, ReplaceDisksMode(..)
......
......@@ -48,6 +48,7 @@ module Ganeti.Query.Language
, ResultValue
, ItemType(..)
, QueryTypeOp(..)
, queryTypeOpToRaw
, QueryTypeLuxi(..)
, checkRS
) where
......
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