diff --git a/htest/Test/Ganeti/Luxi.hs b/htest/Test/Ganeti/Luxi.hs index 8e01bcc9ff7c14871f64d10909114099991d90d2..a788e59cfe40bca91ebe7c3de0e52c1dac44243c 100644 --- a/htest/Test/Ganeti/Luxi.hs +++ b/htest/Test/Ganeti/Luxi.hs @@ -69,7 +69,7 @@ instance Arbitrary Luxi.LuxiOp where listOf getFQDN <*> arbitrary Luxi.ReqQueryConfigValues -> Luxi.QueryConfigValues <$> getFields Luxi.ReqQueryClusterInfo -> pure Luxi.QueryClusterInfo - Luxi.ReqQueryTags -> Luxi.QueryTags <$> arbitrary <*> getFQDN + Luxi.ReqQueryTags -> Luxi.QueryTags <$> arbitrary Luxi.ReqSubmitJob -> Luxi.SubmitJob <$> resize maxOpCodes arbitrary Luxi.ReqSubmitManyJobs -> Luxi.SubmitManyJobs <$> resize maxOpCodes arbitrary diff --git a/htest/Test/Ganeti/OpCodes.hs b/htest/Test/Ganeti/OpCodes.hs index e2bc8a468ce1a869fc4299f728404978482f83d4..728fa76dcfa97321cfa8ae098e5c9c3b6858aaba 100644 --- a/htest/Test/Ganeti/OpCodes.hs +++ b/htest/Test/Ganeti/OpCodes.hs @@ -48,7 +48,12 @@ import qualified Ganeti.OpCodes as OpCodes -- * Arbitrary instances -$(genArbitrary ''OpCodes.TagObject) +instance Arbitrary OpCodes.TagObject where + arbitrary = oneof [ OpCodes.TagInstance <$> getFQDN + , OpCodes.TagNode <$> getFQDN + , OpCodes.TagGroup <$> getFQDN + , pure OpCodes.TagCluster + ] $(genArbitrary ''OpCodes.ReplaceDisksMode) @@ -72,9 +77,9 @@ instance Arbitrary OpCodes.OpCode where OpCodes.OpInstanceMigrate <$> getFQDN <*> arbitrary <*> arbitrary <*> arbitrary <*> getMaybe getFQDN "OP_TAGS_SET" -> - OpCodes.OpTagsSet <$> arbitrary <*> genTags <*> getMaybe getFQDN + OpCodes.OpTagsSet <$> arbitrary <*> genTags "OP_TAGS_DEL" -> - OpCodes.OpTagsSet <$> arbitrary <*> genTags <*> getMaybe getFQDN + OpCodes.OpTagsSet <$> arbitrary <*> genTags _ -> fail "Wrong opcode" -- * Test cases diff --git a/htools/Ganeti/Luxi.hs b/htools/Ganeti/Luxi.hs index 622eceb6c0fefeaaafc193b65ed8c6d31ffc02ac..8ad7f32b74bdf39d918567095a0ea5cf7c74d150 100644 --- a/htools/Ganeti/Luxi.hs +++ b/htools/Ganeti/Luxi.hs @@ -137,8 +137,8 @@ $(genLuxiOp "LuxiOp" ) , (luxiReqQueryClusterInfo, []) , (luxiReqQueryTags, - [ simpleField "kind" [t| TagObject |] - , simpleField "name" [t| String |] + [ customField 'decodeTagObject 'encodeTagObject $ + simpleField "kind" [t| TagObject |] ]) , (luxiReqSubmitJob, [ simpleField "job" [t| [OpCode] |] ] @@ -386,7 +386,8 @@ decodeCall (LuxiCall call args) = return $ QueryConfigValues fields ReqQueryTags -> do (kind, name) <- fromJVal args - return $ QueryTags kind name + item <- tagObjectFrom kind name + return $ QueryTags item ReqCancelJob -> do [job] <- fromJVal args rid <- parseJobId job diff --git a/htools/Ganeti/OpCodes.hs b/htools/Ganeti/OpCodes.hs index 789d84a04f411e0b1e57a2e350c4edf78a49ea45..e2c91810fe49eb1da648a18fe2741d2777869a24 100644 --- a/htools/Ganeti/OpCodes.hs +++ b/htools/Ganeti/OpCodes.hs @@ -28,6 +28,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA module Ganeti.OpCodes ( OpCode(..) , TagObject(..) + , tagObjectFrom + , encodeTagObject + , decodeTagObject , ReplaceDisksMode(..) , DiskIndex , mkDiskIndex @@ -36,7 +39,8 @@ module Ganeti.OpCodes , allOpIDs ) where -import Text.JSON (readJSON, showJSON, makeObj, JSON) +import Text.JSON (readJSON, showJSON, makeObj, JSON, JSValue(..), fromJSString) +import Text.JSON.Pretty (pp_value) import qualified Ganeti.Constants as C import Ganeti.THH @@ -44,13 +48,61 @@ import Ganeti.THH import Ganeti.JSON -- | Data type representing what items do the tag operations apply to. -$(declareSADT "TagObject" - [ ("TagInstance", 'C.tagInstance) - , ("TagNode", 'C.tagNode) - , ("TagGroup", 'C.tagNodegroup) - , ("TagCluster", 'C.tagCluster) +$(declareSADT "TagType" + [ ("TagTypeInstance", 'C.tagInstance) + , ("TagTypeNode", 'C.tagNode) + , ("TagTypeGroup", 'C.tagNodegroup) + , ("TagTypeCluster", 'C.tagCluster) ]) -$(makeJSONInstance ''TagObject) +$(makeJSONInstance ''TagType) + +-- | Data type holding a tag object (type and object name). +data TagObject = TagInstance String + | TagNode String + | TagGroup String + | TagCluster + deriving (Show, Read, Eq) + +-- | Tag type for a given tag object. +tagTypeOf :: TagObject -> TagType +tagTypeOf (TagInstance {}) = TagTypeInstance +tagTypeOf (TagNode {}) = TagTypeNode +tagTypeOf (TagGroup {}) = TagTypeGroup +tagTypeOf (TagCluster {}) = TagTypeCluster + +-- | Gets the potential tag object name. +tagNameOf :: TagObject -> Maybe String +tagNameOf (TagInstance s) = Just s +tagNameOf (TagNode s) = Just s +tagNameOf (TagGroup s) = Just s +tagNameOf TagCluster = Nothing + +-- | Builds a 'TagObject' from a tag type and name. +tagObjectFrom :: (Monad m) => TagType -> JSValue -> m TagObject +tagObjectFrom TagTypeInstance (JSString s) = + return . TagInstance $ fromJSString s +tagObjectFrom TagTypeNode (JSString s) = return . TagNode $ fromJSString s +tagObjectFrom TagTypeGroup (JSString s) = return . TagGroup $ fromJSString s +tagObjectFrom TagTypeCluster JSNull = return TagCluster +tagObjectFrom t v = + fail $ "Invalid tag type/name combination: " ++ show t ++ "/" ++ + show (pp_value v) + +-- | Name of the tag \"name\" field. +tagNameField :: String +tagNameField = "name" + +-- | Custom encoder for 'TagObject' as represented in an opcode. +encodeTagObject :: TagObject -> (JSValue, [(String, JSValue)]) +encodeTagObject t = ( showJSON (tagTypeOf t) + , [(tagNameField, maybe JSNull showJSON (tagNameOf t))] ) + +-- | Custom decoder for 'TagObject' as represented in an opcode. +decodeTagObject :: (Monad m) => [(String, JSValue)] -> JSValue -> m TagObject +decodeTagObject obj kind = do + ttype <- fromJVal kind + tname <- fromObj obj tagNameField + tagObjectFrom ttype tname -- | Replace disks type. $(declareSADT "ReplaceDisksMode" @@ -107,14 +159,14 @@ $(genOpCode "OpCode" , optionalField $ simpleField "target_node" [t| String |] ]) , ("OpTagsSet", - [ simpleField "kind" [t| TagObject |] + [ customField 'decodeTagObject 'encodeTagObject $ + simpleField "kind" [t| TagObject |] , simpleField "tags" [t| [String] |] - , optionalNullSerField $ simpleField "name" [t| String |] ]) , ("OpTagsDel", - [ simpleField "kind" [t| TagObject |] + [ customField 'decodeTagObject 'encodeTagObject $ + simpleField "kind" [t| TagObject |] , simpleField "tags" [t| [String] |] - , optionalNullSerField $ simpleField "name" [t| String |] ]) ]) diff --git a/htools/Ganeti/Query/Server.hs b/htools/Ganeti/Query/Server.hs index 91aa745ca93cb272542743dbfc87938e56aad4a3..6b33a9ea92bc347ed215f2e6416bd01a050085e5 100644 --- a/htools/Ganeti/Query/Server.hs +++ b/htools/Ganeti/Query/Server.hs @@ -138,12 +138,12 @@ handleCall cdata QueryClusterInfo = in return . Ok . J.makeObj $ obj -handleCall cfg (QueryTags kind name) = +handleCall cfg (QueryTags kind) = let tags = case kind of - TagCluster -> Ok . clusterTags $ configCluster cfg - TagGroup -> groupTags <$> Config.getGroup cfg name - TagNode -> nodeTags <$> Config.getNode cfg name - TagInstance -> instTags <$> Config.getInstance cfg name + TagCluster -> Ok . clusterTags $ configCluster cfg + TagGroup name -> groupTags <$> Config.getGroup cfg name + TagNode name -> nodeTags <$> Config.getNode cfg name + TagInstance name -> instTags <$> Config.getInstance cfg name in return (J.showJSON <$> tags) handleCall cfg (Query qkind qfields qfilter) = do