diff --git a/htest/Test/Ganeti/OpCodes.hs b/htest/Test/Ganeti/OpCodes.hs index b9398ecc1ebaf5cdcfd8baaebc44d8c572e7d624..e3ed1f397e13941a6baa13c643846e4d65a80ec3 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 1e8f561263fcb519fe55aba4258636b4d2cc6cd3..f0ffd00f362b12b004d63c5da32381ecf502f1b0 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 e375ae596a17a846845b1b073305d8d09c63fb9a..acb47ba60e3c29d8909800d4b4a8539bbcf4c991 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 c54f0f5a378ba4e0b2474b80b9394f0f9a3df28e..f074439981a78db391bb2061086866a3bba37521 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