borel-0.18.0: lib/Borel/Chevalier.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE MonadComprehensions #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE ViewPatterns #-}
module Borel.Chevalier
( chevalier )
where
import Control.Lens ((^.))
import Control.Monad.Reader
import Data.Either
import Data.Monoid
import Data.Set (Set)
import qualified Data.Set as S
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Encoding as E
import Network.URI
import Pipes as P
import Pipes.Safe as P
import System.Log.Logger
import qualified System.ZMQ4 as Z
import Ceilometer.Tags
import qualified Chevalier.Types as C
import qualified Chevalier.Util as C
import Vaultaire.Types
import Borel.Types
-- | Use Chevalier to find origin, address, sourcedict that contains data relevant
-- to this OpenStack tenancy.
--
chevalier :: (MonadIO m, MonadSafe m)
=> BorelEnv
-> (GroupedMetric, TenancyID)
-> Producer (Origin, Address, SourceDict) m ()
chevalier params (metrics, tid) = do
let tags = chevalierTags
(params ^. paramBorelConfig . allInstances)
(metrics, tid)
when (null tags) $ return ()
liftIO $ debugM "borel" ("searching chevalier with tags=" <> show tags)
P.enumerate
[ (org, addr, sd)
| org <- Select $ P.each (params ^. paramBorelConfig . paramOrigins)
, (addr, sd) <- Select $ searchP
(params ^. paramBorelConfig . paramZMQContext)
(params ^. paramBorelConfig . paramChevalierURI)
org
(C.buildRequestFromPairs tags)
]
-- | Converts a GroupedMetric and a TenancyID into a list of cheavlier tags
-- Also requires a set of the currently configured instance flavors
chevalierTags :: Set Metric -> (GroupedMetric, TenancyID) -> [(Text, Text)]
chevalierTags instances (ms, TenancyID tid) = case ms of
[metric] -> if
| metric == cpu -> [tagID tid , tagName "cpu" ]
| metric == block -> [tagID tid , tagName "volume.size", tagBlock , tagEvent ]
| metric == ssd -> [tagID tid , tagName "volume.size", tagFast , tagEvent ]
| metric == diskReads -> [tagID tid , tagName "disk.read.bytes" ]
| metric == diskWrites -> [tagID tid , tagName "disk.write.bytes" ]
| metric == neutronIn -> [tagID tid , tagName "network.incoming.bytes" ]
| metric == neutronOut -> [tagID tid , tagName "network.outgoing.bytes" ]
| metric == ipv4 -> [tagID tid , tagName "ip.floating" , tagEvent ]
| metric == vcpus -> [tagID tid , tagName "instance_vcpus" , tagPollster ]
| metric == memory -> [tagID tid , tagName "instance_ram" , tagPollster ]
| metric == snapshot -> [tagID tid , tagName "snapshot.size" , tagEvent ]
| metric == image -> [tagID tid , tagName "image.size" , tagPollster ]
| otherwise -> []
_ -> if
| all (`S.member` instances) ms
-> [tagID tid , tagName "instance_flavor" , tagPollster ]
| otherwise -> []
where tagID = (keyTenancyID,)
tagName = (keyMetricName,)
tagEvent = (keyEvent, valTrue)
tagPollster = (keyEvent, valFalse)
tagBlock = (keyVolumeType, valVolumeBlock)
tagFast = (keyVolumeType, valVolumeFast)
--------------------------------------------------------------------------------
searchP
:: (MonadIO m)
=> Z.Context -> URI
-> Origin -> C.SourceRequest
-> Producer (Address, SourceDict) m ()
searchP ctx uri org req = do
x <- lift $ search ctx uri org req
liftIO $ debugM "borel" ("addresses=" <> show (map fst x))
P.each x
-- this doesn't stream because chevalier doesn't
search
:: (MonadIO m)
=> Z.Context -> URI
-> Origin -> C.SourceRequest
-> m [(Address, SourceDict)]
search ctx uri origin request
= liftIO
$ runChevalier ctx uri
$ \conn -> liftIO $ do
resp <- liftIO (sendrecv conn)
return $ either (error . show) (rights . map C.convertSource) (C.decodeResponse resp)
where sendrecv sock = do
Z.send sock [Z.SendMore] $ encodeOrigin origin
Z.send sock [] $ C.encodeRequest request
Z.receive sock
encodeOrigin (Origin x) = E.encodeUtf8 $ T.pack $ show x
type Chevalier = Z.Socket Z.Req
runChevalier
:: Z.Context -> URI
-> (Chevalier -> IO x)
-> IO x
runChevalier ctx (show -> uri) act
= Z.withSocket ctx Z.Req $ \sock -> do
Z.connect sock uri
act sock