@@ -60,6 +60,8 @@ module Database.Bloodhound.Types
6060 , mkBHEnv
6161 , MonadBH(..)
6262 , Version(..)
63+ , VersionNumber(..)
64+ , BuildHash(..)
6365 , Status(..)
6466 , Existence(..)
6567 , NullValue(..)
@@ -146,6 +148,8 @@ module Database.Bloodhound.Types
146148 , Script(..)
147149 , IndexName(..)
148150 , IndexSelection(..)
151+ , NodeSelection(..)
152+ , NodeSelector(..)
149153 , IndexOptimizationSettings(..)
150154 , defaultIndexOptimizationSettings
151155 , TemplateName(..)
@@ -252,6 +256,31 @@ module Database.Bloodhound.Types
252256 , SnapshotNodeVerification(..)
253257 , FullNodeId(..)
254258 , NodeName(..)
259+ , ClusterName(..)
260+ , NodesInfo(..)
261+ , EsAddress(..)
262+ , PluginName(..)
263+ , NodeInfo(..)
264+ , NodePluginInfo(..)
265+ , NodeHTTPInfo(..)
266+ , NodeTransportInfo(..)
267+ , BoundTransportAddress(..)
268+ , NodeNetworkInfo(..)
269+ , MacAddress(..)
270+ , NetworkInterfaceName(..)
271+ , NodeNetworkInterface(..)
272+ , NodeThreadPoolsInfo(..)
273+ , NodeThreadPoolInfo(..)
274+ , ThreadPoolSize(..)
275+ , ThreadPoolType(..)
276+ , NodeJVMInfo(..)
277+ , JVMMemoryPool(..)
278+ , JVMGCCollector(..)
279+ , JVMMemoryInfo(..)
280+ , PID(..)
281+ , NodeOSInfo(..)
282+ , CPUInfo(..)
283+ , NodeProcessInfo(..)
255284 , FsSnapshotRepo(..)
256285 , SnapshotCreateSettings(..)
257286 , defaultSnapshotCreateSettings
@@ -311,6 +340,7 @@ module Database.Bloodhound.Types
311340 ) where
312341
313342import Control.Applicative as A
343+import Control.Arrow (first)
314344import Control.Monad.Catch
315345import Control.Monad.Except
316346import Control.Monad.Reader
@@ -323,7 +353,7 @@ import qualified Data.ByteString.Lazy.Char8 as L
323353import Data.Char
324354import Data.Hashable (Hashable)
325355import qualified Data.HashMap.Strict as HM
326−import Data.List (foldl', nub)
356+import Data.List (foldl', intercalate, nub)
327357import Data.List.NonEmpty (NonEmpty (..), toList)
328358import qualified Data.Map.Strict as M
329359import Data.Maybe
@@ -336,10 +366,12 @@ import Data.Time.Clock.POSIX
336366import qualified Data.Traversable as DT
337367import Data.Typeable (Typeable)
338368import qualified Data.Vector as V
369+import qualified Data.Version as Vers
339370import GHC.Enum
340371import GHC.Generics (Generic)
341372import Network.HTTP.Client
342373import qualified Network.HTTP.Types.Method as NHTM
374+import qualified Text.ParserCombinators.ReadP as RP
343375import qualified Text.Read as TR
344376
345377import Database.Bloodhound.Types.Class
@@ -395,11 +427,15 @@ runBH :: BHEnv -> BH m a -> m a
395427runBH e f = runReaderT (unBH f) e
396428
397429{-| 'Version' is embedded in 'Status' -}
398−data Version = Version { number :: Text
399− , build_hash :: Text
430+data Version = Version { number :: VersionNumber
431+ , build_hash :: BuildHash
400432 , build_timestamp :: UTCTime
401433 , build_snapshot :: Bool
402− , lucene_version :: Text } deriving (Eq, Read, Show, Generic, Typeable)
434+ , lucene_version :: VersionNumber } deriving (Eq, Read, Show, Generic, Typeable)
435+
436+-- | Traditional software versioning number
437+newtype VersionNumber = VersionNumber { versionNumber :: Vers.Version
438+ } deriving (Eq, Read, Show, Generic, Typeable, Ord)
403439
404440{-| 'Status' is a data type for describing the JSON body returned by
405441 Elasticsearch when you query its status. This was deprecated in 1.2.0.
@@ -865,6 +901,25 @@ newtype IndexName = IndexName Text deriving (Eq, Generic, Read, Show, ToJSON, Fr
865901data IndexSelection = IndexList (NonEmpty IndexName)
866902 | AllIndexes deriving (Eq, Generic, Show, Typeable)
867903
904+{-| 'NodeSelection' is used for most cluster APIs. See <https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster.html#cluster-nodes here> for more details.
905+-}
906+data NodeSelection = LocalNode
907+ -- ^ Whatever node receives this request
908+ | NodeList (NonEmpty NodeSelector)
909+ | AllNodes deriving (Eq, Generic, Show, Typeable)
910+
911+
912+-- | An exact match or pattern to identify a node. Note that All of
913+-- these options support wildcarding, so your node name, server, attr
914+-- name can all contain * characters to be a fuzzy match.
915+data NodeSelector = NodeByName NodeName
916+ | NodeByFullNodeId FullNodeId
917+ | NodeByHost Server
918+ -- ^ e.g. 10.0.0.1 or even 10.0.0.*
919+ | NodeByAttribute NodeAttrName Text
920+ -- ^ NodeAttrName can be a pattern, e.g. rack*. The value can too.
921+ deriving (Eq, Generic, Show, Typeable)
922+
868923{-| 'TemplateName' is used to describe which template to query/create/delete
869924-}
870925newtype TemplateName = TemplateName Text deriving (Eq, Read, Show, Generic, ToJSON, FromJSON, Typeable)
@@ -1735,6 +1790,17 @@ instance FromJSON Version where
17351790 <*> o .: "build_snapshot"
17361791 <*> o .: "lucene_version"
17371792
1793+instance ToJSON VersionNumber where
1794+ toJSON = toJSON . Vers.showVersion . versionNumber
1795+
1796+instance FromJSON VersionNumber where
1797+ parseJSON = withText "VersionNumber" (parse . T.unpack)
1798+ where
1799+ parse s = case filter (null . snd)(RP.readP_to_S Vers.parseVersion s) of
1800+ [(v, _)] -> pure (VersionNumber v)
1801+ [] -> fail ("Invalid version string " ++ s)
1802+ xs -> fail ("Ambiguous version string " ++ s ++ " (" ++ intercalate ", " (Vers.showVersion . fst <$> xs) ++ ")")
1803+
17381804instance ToJSON TermOrder where
17391805 toJSON (TermOrder termSortField termSortOrder) = object [termSortField .= termSortOrder]
17401806
@@ -3767,6 +3833,189 @@ newtype FullNodeId = FullNodeId { fullNodeId :: Text }
37673833newtype NodeName = NodeName { nodeName :: Text }
37683834 deriving (Eq, Ord, Generic, Show, Typeable, FromJSON)
37693835
3836+newtype ClusterName = ClusterName { clusterName :: Text }
3837+ deriving (Eq, Ord, Generic, Show, Typeable, FromJSON)
3838+
3839+data NodesInfo = NodesInfo {
3840+ nodesInfo :: [NodeInfo]
3841+ , nodesClusterName :: ClusterName
3842+ } deriving (Eq, Show, Generic, Typeable)
3843+
3844+-- | A quirky address format used throughout ElasticSearch. An example
3845+-- would be inet[/1.1.1.1:9200]. inet may be a placeholder for a
3846+-- <https://en.wikipedia.org/wiki/Fully_qualified_domain_name FQDN>.
3847+newtype EsAddress = EsAddress { esAddress :: Text }
3848+ deriving (Eq, Ord, Generic, Show, Typeable, FromJSON)
3849+
3850+-- | Typically a 7 character hex string.
3851+newtype BuildHash = BuildHash { buildHash :: Text }
3852+ deriving (Eq, Ord, Generic, Read, Show, Typeable, FromJSON, ToJSON)
3853+
3854+newtype PluginName = PluginName { pluginName :: Text }
3855+ deriving (Eq, Ord, Generic, Show, Typeable, FromJSON)
3856+
3857+data NodeInfo = NodeInfo {
3858+ nodeInfoHTTPAddress :: EsAddress
3859+ , nodeInfoBuild :: BuildHash
3860+ , nodeInfoESVersion :: VersionNumber
3861+ , nodeInfoIP :: Server
3862+ , nodeInfoHost :: Server
3863+ , nodeInfoTransportAddress :: EsAddress
3864+ , nodeInfoName :: NodeName
3865+ , nodeInfoFullId :: FullNodeId
3866+ , nodeInfoPlugins :: [NodePluginInfo]
3867+ , nodeInfoHTTP :: NodeHTTPInfo
3868+ , nodeInfoTransport :: NodeTransportInfo
3869+ , nodeInfoNetwork :: NodeNetworkInfo
3870+ , nodeInfoThreadPool :: NodeThreadPoolsInfo
3871+ , nodeInfoJVM :: NodeJVMInfo
3872+ , nodeInfoProcess :: NodeProcessInfo
3873+ , nodeInfoOS :: NodeOSInfo
3874+ , nodeInfoSettings :: Object
3875+ -- ^ The members of the settings objects are not consistent,
3876+ -- dependent on plugins, etc.
3877+ } deriving (Eq, Show, Generic, Typeable)
3878+
3879+data NodePluginInfo = NodePluginInfo {
3880+ nodePluginSite :: Bool
3881+ -- ^ Is this a site plugin?
3882+ , nodePluginJVM :: Bool
3883+ -- ^ Is this plugin running on the JVM
3884+ , nodePluginDescription :: Text
3885+ , nodePluginVersion :: VersionNumber
3886+ , nodePluginName :: PluginName
3887+ } deriving (Eq, Show, Generic, Typeable)
3888+
3889+data NodeHTTPInfo = NodeHTTPInfo {
3890+ nodeHTTPMaxContentLength :: Bytes
3891+ , nodeHTTPTransportAddress :: BoundTransportAddress
3892+ } deriving (Eq, Show, Generic, Typeable)
3893+
3894+data NodeTransportInfo = NodeTransportInfo {
3895+ nodeTransportProfiles :: [BoundTransportAddress]
3896+ , nodeTransportAddress :: BoundTransportAddress
3897+ } deriving (Eq, Show, Generic, Typeable)
3898+
3899+data BoundTransportAddress = BoundTransportAddress {
3900+ publishAddress :: EsAddress
3901+ , boundAddress :: EsAddress
3902+ } deriving (Eq, Show, Generic, Typeable)
3903+
3904+data NodeNetworkInfo = NodeNetworkInfo {
3905+ nodeNetworkPrimaryInterface :: NodeNetworkInterface
3906+ , nodeNetworkRefreshInterval :: NominalDiffTime
3907+ } deriving (Eq, Show, Generic, Typeable)
3908+
3909+newtype MacAddress = MacAddress { macAddress :: Text }
3910+ deriving (Eq, Ord, Generic, Show, Typeable, FromJSON)
3911+
3912+newtype NetworkInterfaceName = NetworkInterfaceName { networkInterfaceName :: Text }
3913+ deriving (Eq, Ord, Generic, Show, Typeable, FromJSON)
3914+
3915+data NodeNetworkInterface = NodeNetworkInterface {
3916+ nodeNetIfaceMacAddress :: MacAddress
3917+ , nodeNetIfaceName :: NetworkInterfaceName
3918+ , nodeNetIfaceAddress :: Server
3919+ } deriving (Eq, Show, Generic, Typeable)
3920+
3921+data NodeThreadPoolsInfo = NodeThreadPoolsInfo {
3922+ nodeThreadPoolsRefresh :: NodeThreadPoolInfo
3923+ , nodeThreadPoolsManagement :: NodeThreadPoolInfo
3924+ , nodeThreadPoolsPercolate :: NodeThreadPoolInfo
3925+ , nodeThreadPoolsListener :: Maybe NodeThreadPoolInfo
3926+ , nodeThreadPoolsFetchShardStarted :: Maybe NodeThreadPoolInfo
3927+ , nodeThreadPoolsSearch :: NodeThreadPoolInfo
3928+ , nodeThreadPoolsFlush :: NodeThreadPoolInfo
3929+ , nodeThreadPoolsWarmer :: NodeThreadPoolInfo
3930+ , nodeThreadPoolsOptimize :: NodeThreadPoolInfo
3931+ , nodeThreadPoolsBulk :: NodeThreadPoolInfo
3932+ , nodeThreadPoolsSuggest :: NodeThreadPoolInfo
3933+ , nodeThreadPoolsMerge :: NodeThreadPoolInfo
3934+ , nodeThreadPoolsSnapshot :: NodeThreadPoolInfo
3935+ , nodeThreadPoolsGet :: NodeThreadPoolInfo
3936+ , nodeThreadPoolsFetchShardStore :: Maybe NodeThreadPoolInfo
3937+ , nodeThreadPoolsIndex :: NodeThreadPoolInfo
3938+ , nodeThreadPoolsGeneric :: NodeThreadPoolInfo
3939+ } deriving (Eq, Show, Generic, Typeable)
3940+
3941+data NodeThreadPoolInfo = NodeThreadPoolInfo {
3942+ nodeThreadPoolQueueSize :: ThreadPoolSize
3943+ , nodeThreadPoolKeepalive :: Maybe NominalDiffTime
3944+ , nodeThreadPoolMin :: Maybe Int
3945+ , nodeThreadPoolMax :: Maybe Int
3946+ , nodeThreadPoolType :: ThreadPoolType
3947+ } deriving (Eq, Show, Generic, Typeable)
3948+
3949+data ThreadPoolSize = ThreadPoolBounded Int
3950+ | ThreadPoolUnbounded
3951+ deriving (Eq, Show, Generic, Typeable)
3952+
3953+data ThreadPoolType = ThreadPoolScaling
3954+ | ThreadPoolFixed
3955+ | ThreadPoolCached
3956+ deriving (Eq, Show, Generic, Typeable)
3957+
3958+data NodeJVMInfo = NodeJVMInfo {
3959+ nodeJVMInfoMemoryPools :: [JVMMemoryPool]
3960+ , nodeJVMInfoMemoryPoolsGCCollectors :: [JVMGCCollector]
3961+ , nodeJVMInfoMemoryInfo :: JVMMemoryInfo
3962+ , nodeJVMInfoStartTime :: UTCTime
3963+ , nodeJVMInfoVMVendor :: Text
3964+ , nodeJVMVMVersion :: VersionNumber
3965+ -- ^ JVM doesn't seme to follow normal version conventions
3966+ , nodeJVMVMName :: Text
3967+ , nodeJVMVersion :: VersionNumber
3968+ , nodeJVMPID :: PID
3969+ } deriving (Eq, Show, Generic, Typeable)
3970+
3971+-- | Handles quirks in the way JVM versions are rendered (1.7.0_101 -> 1.7.0.101)
3972+newtype JVMVersion = JVMVersion { unJVMVersion :: VersionNumber }
3973+
3974+data JVMMemoryInfo = JVMMemoryInfo {
3975+ jvmMemoryInfoDirectMax :: Bytes
3976+ , jvmMemoryInfoNonHeapMax :: Bytes
3977+ , jvmMemoryInfoNonHeapInit :: Bytes
3978+ , jvmMemoryInfoHeapMax :: Bytes
3979+ , jvmMemoryInfoHeapInit :: Bytes
3980+ } deriving (Eq, Show, Generic, Typeable)
3981+
3982+newtype JVMMemoryPool = JVMMemoryPool {
3983+ jvmMemoryPool :: Text
3984+ } deriving (Eq, Show, Generic, Typeable, FromJSON)
3985+
3986+newtype JVMGCCollector = JVMGCCollector {
3987+ jvmGCCollector :: Text
3988+ } deriving (Eq, Show, Generic, Typeable, FromJSON)
3989+
3990+newtype PID = PID {
3991+ pid :: Int
3992+ } deriving (Eq, Show, Generic, Typeable, FromJSON)
3993+
3994+data NodeOSInfo = NodeOSInfo {
3995+ nodeOSSwap :: Bytes
3996+ , nodeOSMem :: Bytes
3997+ , nodeOSCPUInfo :: CPUInfo
3998+ , nodeOSAvailableProcessors :: Int
3999+ , nodeOSRefreshInterval :: NominalDiffTime
4000+ } deriving (Eq, Show, Generic, Typeable)
4001+
4002+data CPUInfo = CPUInfo {
4003+ cpuCacheSize :: Bytes
4004+ , cpuCoresPerSocket :: Int
4005+ , cpuTotalSockets :: Int
4006+ , cpuTotalCores :: Int
4007+ , cpuMHZ :: Int
4008+ , cpuModel :: Text
4009+ , cpuVendor :: Text
4010+ } deriving (Eq, Show, Generic, Typeable)
4011+
4012+data NodeProcessInfo = NodeProcessInfo {
4013+ nodeProcessMLockAll :: Bool
4014+ -- ^ See <https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-configuration.html>
4015+ , nodeProcessMaxFileDescriptors :: Int
4016+ , nodeProcessId :: PID
4017+ , nodeProcessRefreshInterval :: NominalDiffTime
4018+ } deriving (Eq, Show, Generic, Typeable)
37704019
37714020data SnapshotRepoUpdateSettings = SnapshotRepoUpdateSettings {
37724021 repoUpdateVerify :: Bool
@@ -3923,9 +4172,9 @@ instance FromJSON SnapshotInfo where
39234172 <*> o .: "snapshot"
39244173
39254174data SnapshotShardFailure = SnapshotShardFailure {
3926− snapShardFailureIndex :: IndexName
3927− , snapShardFailureNodeId :: Maybe NodeName -- I'm not 100% sure this isn't actually 'FullNodeId'
3928− , snapShardFailureReason :: Text
4175+ snapShardFailureIndex :: IndexName
4176+ , snapShardFailureNodeId :: Maybe NodeName -- I'm not 100% sure this isn't actually 'FullNodeId'
4177+ , snapShardFailureReason :: Text
39294178 , snapShardFailureShardId :: ShardId
39304179 } deriving (Eq, Show, Generic, Typeable)
39314180
@@ -3984,37 +4233,37 @@ newtype SnapshotName = SnapshotName { snapshotName :: Text }
39844233
39854234
39864235data SnapshotRestoreSettings = SnapshotRestoreSettings {
3987− snapRestoreWaitForCompletion :: Bool
4236+ snapRestoreWaitForCompletion :: Bool
39884237 -- ^ Should the API call return immediately after initializing
39894238 -- the restore or wait until completed? Note that if this is
39904239 -- enabled, it could wait a long time, so you should adjust your
39914240 -- 'ManagerSettings' accordingly to set long timeouts or
39924241 -- explicitly handle timeouts.
3993− , snapRestoreIndices :: Maybe IndexSelection
4242+ , snapRestoreIndices :: Maybe IndexSelection
39944243 -- ^ Nothing will restore all indices in the snapshot. Just [] is
39954244 -- permissable and will essentially be a no-op restore.
3996− , snapRestoreIgnoreUnavailable :: Bool
4245+ , snapRestoreIgnoreUnavailable :: Bool
39974246 -- ^ If set to True, any indices that do not exist will be ignored
39984247 -- during snapshot rather than failing the restore.
3999− , snapRestoreIncludeGlobalState :: Bool
4248+ , snapRestoreIncludeGlobalState :: Bool
40004249 -- ^ If set to false, will ignore any global state in the snapshot
40014250 -- and will not restore it.
4002− , snapRestoreRenamePattern :: Maybe RestoreRenamePattern
4251+ , snapRestoreRenamePattern :: Maybe RestoreRenamePattern
40034252 -- ^ A regex pattern for matching indices. Used with
40044253 -- 'snapRestoreRenameReplacement', the restore can reference the
40054254 -- matched index and create a new index name upon restore.
4006− , snapRestoreRenameReplacement :: Maybe (NonEmpty RestoreRenameToken)
4255+ , snapRestoreRenameReplacement :: Maybe (NonEmpty RestoreRenameToken)
40074256 -- ^ Expression of how index renames should be constructed.
4008− , snapRestorePartial :: Bool
4257+ , snapRestorePartial :: Bool
40094258 -- ^ If some indices fail to restore, should the process proceed?
4010− , snapRestoreIncludeAliases :: Bool
4259+ , snapRestoreIncludeAliases :: Bool
40114260 -- ^ Should the restore also restore the aliases captured in the
40124261 -- snapshot.
40134262 , snapRestoreIndexSettingsOverrides :: Maybe RestoreIndexSettings
40144263 -- ^ Settings to apply during the restore process. __NOTE:__ This
40154264 -- option is not supported in ES < 1.5 and should be set to
40164265 -- Nothing in that case.
4017− , snapRestoreIgnoreIndexSettings :: Maybe (NonEmpty Text)
4266+ , snapRestoreIgnoreIndexSettings :: Maybe (NonEmpty Text)
40184267 -- ^ This type could be more rich but it isn't clear which
40194268 -- settings are allowed to be ignored during restore, so we're
40204269 -- going with including this feature in a basic form rather than
@@ -4098,3 +4347,208 @@ instance ToJSON RestoreIndexSettings where
40984347 toJSON RestoreIndexSettings {..} = object prs
40994348 where
41004349 prs = catMaybes [("index.number_of_replicas" .=) <$> restoreOverrideReplicas]
4350+
4351+
4352+instance FromJSON NodesInfo where
4353+ parseJSON = withObject "NodesInfo" parse
4354+ where
4355+ parse o = do
4356+ nodes <- o .: "nodes"
4357+ infos <- forM (HM.toList nodes) $ \(fullNID, v) -> do
4358+ node <- parseJSON v
4359+ parseNodeInfo (FullNodeId fullNID) node
4360+ cn <- o .: "cluster_name"
4361+ return (NodesInfo infos cn)
4362+
4363+
4364+parseNodeInfo :: FullNodeId -> Object -> Parser NodeInfo
4365+parseNodeInfo nid o =
4366+ NodeInfo <$> o .: "http_address"
4367+ <*> o .: "build"
4368+ <*> o .: "version"
4369+ <*> o .: "ip"
4370+ <*> o .: "host"
4371+ <*> o .: "transport_address"
4372+ <*> o .: "name"
4373+ <*> pure nid
4374+ <*> o .: "plugins"
4375+ <*> o .: "http"
4376+ <*> o .: "transport"
4377+ <*> o .: "network"
4378+ <*> o .: "thread_pool"
4379+ <*> o .: "jvm"
4380+ <*> o .: "process"
4381+ <*> o .: "os"
4382+ <*> o .: "settings"
4383+
4384+instance FromJSON NodePluginInfo where
4385+ parseJSON = withObject "NodePluginInfo" parse
4386+ where
4387+ parse o = NodePluginInfo <$> o .: "site"
4388+ <*> o .: "jvm"
4389+ <*> o .: "description"
4390+ <*> o .: "version"
4391+ <*> o .: "name"
4392+
4393+instance FromJSON NodeHTTPInfo where
4394+ parseJSON = withObject "NodeHTTPInfo" parse
4395+ where
4396+ parse o = NodeHTTPInfo <$> o .: "max_content_length_in_bytes"
4397+ <*> parseJSON (Object o)
4398+
4399+instance FromJSON BoundTransportAddress where
4400+ parseJSON = withObject "BoundTransportAddress" parse
4401+ where
4402+ parse o = BoundTransportAddress <$> o .: "publish_address"
4403+ <*> o .: "bound_address"
4404+
4405+instance FromJSON NodeOSInfo where
4406+ parseJSON = withObject "NodeOSInfo" parse
4407+ where
4408+ parse o = do
4409+ swap <- o .: "swap"
4410+ mem <- o .: "mem"
4411+ NodeOSInfo <$> swap .: "total_in_bytes"
4412+ <*> mem .: "total_in_bytes"
4413+ <*> o .: "cpu"
4414+ <*> o .: "available_processors"
4415+ <*> (unMS <$> o .: "refresh_interval_in_millis")
4416+
4417+
4418+instance FromJSON CPUInfo where
4419+ parseJSON = withObject "CPUInfo" parse
4420+ where
4421+ parse o = CPUInfo <$> o .: "cache_size_in_bytes"
4422+ <*> o .: "cores_per_socket"
4423+ <*> o .: "total_sockets"
4424+ <*> o .: "total_cores"
4425+ <*> o .: "mhz"
4426+ <*> o .: "model"
4427+ <*> o .: "vendor"
4428+
4429+instance FromJSON NodeProcessInfo where
4430+ parseJSON = withObject "NodeProcessInfo" parse
4431+ where
4432+ parse o = NodeProcessInfo <$> o .: "mlockall"
4433+ <*> o .: "max_file_descriptors"
4434+ <*> o .: "id"
4435+ <*> (unMS <$> o .: "refresh_interval_in_millis")
4436+
4437+instance FromJSON NodeJVMInfo where
4438+ parseJSON = withObject "NodeJVMInfo" parse
4439+ where
4440+ parse o = NodeJVMInfo <$> o .: "memory_pools"
4441+ <*> o .: "gc_collectors"
4442+ <*> o .: "mem"
4443+ <*> (posixMS <$> o .: "start_time_in_millis")
4444+ <*> o .: "vm_vendor"
4445+ <*> o .: "vm_version"
4446+ <*> o .: "vm_name"
4447+ <*> (unJVMVersion <$> o .: "version")
4448+ <*> o .: "pid"
4449+
4450+instance FromJSON JVMVersion where
4451+ parseJSON (String t) =
4452+ JVMVersion <$> parseJSON (String (T.replace "_" "." t))
4453+ parseJSON v = JVMVersion <$> parseJSON v
4454+
4455+instance FromJSON JVMMemoryInfo where
4456+ parseJSON = withObject "JVMMemoryInfo" parse
4457+ where
4458+ parse o = JVMMemoryInfo <$> o .: "direct_max_in_bytes"
4459+ <*> o .: "non_heap_max_in_bytes"
4460+ <*> o .: "non_heap_init_in_bytes"
4461+ <*> o .: "heap_max_in_bytes"
4462+ <*> o .: "heap_init_in_bytes"
4463+
4464+instance FromJSON NodeThreadPoolsInfo where
4465+ parseJSON = withObject "NodeThreadPoolsInfo" parse
4466+ where
4467+ parse o = NodeThreadPoolsInfo <$> o .: "refresh"
4468+ <*> o .: "management"
4469+ <*> o .: "percolate"
4470+ <*> o .:? "listener"
4471+ <*> o .:? "fetch_shard_started"
4472+ <*> o .: "search"
4473+ <*> o .: "flush"
4474+ <*> o .: "warmer"
4475+ <*> o .: "optimize"
4476+ <*> o .: "bulk"
4477+ <*> o .: "suggest"
4478+ <*> o .: "merge"
4479+ <*> o .: "snapshot"
4480+ <*> o .: "get"
4481+ <*> o .:? "fetch_shard_store"
4482+ <*> o .: "index"
4483+ <*> o .: "generic"
4484+
4485+instance FromJSON NodeThreadPoolInfo where
4486+ parseJSON = withObject "NodeThreadPoolInfo" parse
4487+ where
4488+ parse o = do
4489+ ka <- maybe (return Nothing) (fmap Just . parseStringInterval) =<< o .:? "keep_alive"
4490+ NodeThreadPoolInfo <$> (parseJSON . unStringlyTypeJSON =<< o .: "queue_size")
4491+ <*> pure ka
4492+ <*> o .:? "min"
4493+ <*> o .:? "max"
4494+ <*> o .: "type"
4495+
4496+parseStringInterval :: (Monad m) => String -> m NominalDiffTime
4497+parseStringInterval s = case span isNumber s of
4498+ ("", _) -> fail "Invalid interval"
4499+ (nS, unitS) -> case (readMay nS, readMay unitS) of
4500+ (Just n, Just unit) -> return (fromInteger (n * unitNDT unit))
4501+ (Nothing, _) -> fail "Invalid interval number"
4502+ (_, Nothing) -> fail "Invalid interval unit"
4503+ where
4504+ unitNDT Seconds = 1
4505+ unitNDT Minutes = 60
4506+ unitNDT Hours = 60 * 60
4507+ unitNDT Days = 24 * 60 * 60
4508+ unitNDT Weeks = 7 * 24 * 60 * 60
4509+
4510+instance FromJSON ThreadPoolSize where
4511+ parseJSON v = parseAsNumber v <|> parseAsString v
4512+ where
4513+ parseAsNumber = parseAsInt <=< parseJSON
4514+ parseAsInt (-1) = return ThreadPoolUnbounded
4515+ parseAsInt n
4516+ | n >= 0 = return (ThreadPoolBounded n)
4517+ | otherwise = fail "Thread pool size must be >= -1."
4518+ parseAsString = withText "ThreadPoolSize" $ \t ->
4519+ case first (readMay . T.unpack) (T.span isNumber t) of
4520+ (Just n, "k") -> return (ThreadPoolBounded (n * 1000))
4521+ (Just n, "") -> return (ThreadPoolBounded n)
4522+ _ -> fail ("Invalid thread pool size " <> T.unpack t)
4523+
4524+instance FromJSON ThreadPoolType where
4525+ parseJSON = withText "ThreadPoolType" parse
4526+ where
4527+ parse "scaling" = return ThreadPoolScaling
4528+ parse "fixed" = return ThreadPoolFixed
4529+ parse "cached" = return ThreadPoolCached
4530+ parse e = fail ("Unexpected thread pool type" <> T.unpack e)
4531+
4532+instance FromJSON NodeTransportInfo where
4533+ parseJSON = withObject "NodeTransportInfo" parse
4534+ where
4535+ parse o = NodeTransportInfo <$> (maybe (return mempty) parseProfiles =<< o .:? "profiles")
4536+ <*> parseJSON (Object o)
4537+ parseProfiles (Object o) | HM.null o = return []
4538+ parseProfiles v@(Array _) = parseJSON v
4539+ parseProfiles Null = return []
4540+ parseProfiles _ = fail "Could not parse profiles"
4541+
4542+instance FromJSON NodeNetworkInfo where
4543+ parseJSON = withObject "NodeNetworkInfo" parse
4544+ where
4545+ parse o = NodeNetworkInfo <$> o .: "primary_interface"
4546+ <*> (unMS <$> o .: "refresh_interval_in_millis")
4547+
4548+
4549+instance FromJSON NodeNetworkInterface where
4550+ parseJSON = withObject "NodeNetworkInterface" parse
4551+ where
4552+ parse o = NodeNetworkInterface <$> o .: "mac_address"
4553+ <*> o .: "name"
4554+ <*> o .: "address"