cql-io-2.0.0: src/lib/Database/CQL/IO/Cluster/Host.hs
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at http://mozilla.org/MPL/2.0/.
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
module Database.CQL.IO.Cluster.Host where
import Control.Lens (Lens', (^.))
import Database.CQL.Protocol (Response (..))
import Database.CQL.IO.Cluster.Discovery hiding (broadcastAddr, rack, hostId)
import qualified Database.CQL.IO.Cluster.Discovery as Disco (broadcastAddr, rack, hostId)
import Data.IP
import Data.Text (Text, unpack)
import Network.Socket (SockAddr (..), PortNumber)
import Data.UUID (UUID, nil)
-- | A Cassandra host known to the client.
data Host = Host
{ _hostAddr :: !InetAddr
, _broadcastAddr :: !InetAddr
, _hostId :: !UUID
, _dataCentre :: !Text
, _rack :: !Text
}
dummyHost :: InetAddr -> Host
dummyHost a = Host
{ _hostAddr = a
, _broadcastAddr = a
, _hostId = nil
, _dataCentre = ""
, _rack = ""
}
instance Eq Host where
a == b = _hostAddr a == _hostAddr b
instance Ord Host where
compare a b = compare (_hostAddr a) (_hostAddr b)
peer2Host :: PortNumber -> Peer -> Host
peer2Host i p = Host
{ _hostAddr = ip2inet i (peerRPC p)
, _broadcastAddr = ip2inet 7000 (peerAddr p)
, _hostId = peerHostId p
, _dataCentre = peerDC p
, _rack = peerRack p
}
peer2HostV2 :: PeerV2 -> Host
peer2HostV2 peer2 =
let nativePortNum = fromIntegral . peerV2NativePort $ peer2
broadcastPortNum = fromIntegral . peerV2Port $ peer2
in Host
{ _hostAddr = ip2inet nativePortNum (peerV2NativeAddr peer2)
, _broadcastAddr = ip2inet broadcastPortNum (peerV2Addr peer2)
, _hostId = peerV2HostId peer2
, _dataCentre = peerV2DC peer2
, _rack = peerV2Rack peer2
}
local2Host :: PortNumber -> Local -> Host
local2Host settingsPort curLocal =
let rpcPortNum = case curLocal of
LocalV3_ _ -> settingsPort
LocalV4_ l4 -> fromIntegral $ l4 ^. rpcPort
broadcastPortNum = case curLocal of
LocalV3_ _ -> 7000
LocalV4_ l4 -> fromIntegral $ l4 ^. broadcastPort
in Host
{ _hostAddr = ip2inet rpcPortNum (curLocal^.rpcAddr)
, _broadcastAddr = ip2inet broadcastPortNum (curLocal ^. Disco.broadcastAddr)
, _hostId = curLocal ^. Disco.hostId
, _dataCentre = curLocal^.dC
, _rack = curLocal ^. Disco.rack
}
updateHost :: Host -> Maybe (Text, Text) -> Host
updateHost h (Just (dc, rk)) = h { _dataCentre = dc, _rack = rk }
updateHost h Nothing = h
-- | A response that is known to originate from a specific 'Host'.
data HostResponse k a b = HostResponse
{ hrHost :: !Host
, hrResponse :: !(Response k a b)
} deriving (Show)
-- | This event will be passed to a 'Policy' to inform it about
-- cluster changes.
data HostEvent
= HostNew !Host -- ^ a new host has been added to the cluster
| HostGone !Host -- ^ a host has been removed from the cluster
| HostUp !Host -- ^ a host has been started
| HostDown !Host -- ^ a host has been stopped
| AddrDown !InetAddr -- ^ an address was reported down, we could not find the corresponding host.
-- | The IP address and port number of a host.
hostAddr :: Lens' Host InetAddr
hostAddr f h = fmap (\x -> h {_hostAddr = x}) (f (_hostAddr h))
{-# INLINE hostAddr #-}
-- | The IP address and port number that will be used for events about a host.
broadcastAddr :: Lens' Host InetAddr
broadcastAddr f h = fmap (\x -> h {_broadcastAddr = x}) (f (_broadcastAddr h))
{-# INLINE broadcastAddr #-}
-- | The UUID hostId of a host.
hostId :: Lens' Host UUID
hostId f h = fmap (\x -> h {_hostId = x}) (f (_hostId h))
{-# INLINE hostId #-}
-- | The data centre name (may be an empty string).
dataCentre :: Lens' Host Text
dataCentre f h = fmap (\x -> h {_dataCentre = x}) (f (_dataCentre h))
{-# INLINE dataCentre #-}
-- | The rack name (may be an empty string).
rack :: Lens' Host Text
rack f h = fmap (\x -> h {_rack = x}) (f (_rack h))
{-# INLINE rack #-}
instance Show Host where
show h = showString (unpack (_dataCentre h))
. showString ":"
. showString (unpack (_rack h))
. showString ":"
. shows (_hostAddr h)
. showString ":"
. shows (_broadcastAddr h)
$ ""
-----------------------------------------------------------------------------
-- InetAddr
newtype InetAddr = InetAddr { sockAddr :: SockAddr }
deriving (Eq, Ord)
instance Show InetAddr where
show (InetAddr (SockAddrInet p a)) =
let i = fromIntegral p :: Int in
shows (fromHostAddress a) . showString ":" . shows i $ ""
show (InetAddr (SockAddrInet6 p _ a _)) =
let i = fromIntegral p :: Int in
shows (fromHostAddress6 a) . showString ":" . shows i $ ""
show (InetAddr (SockAddrUnix unix)) = unix
#if MIN_VERSION_network(2,6,1) && !MIN_VERSION_network(3,0,0)
show (InetAddr (SockAddrCan int32)) = show int32
#endif
-- | Map a 'SockAddr' into an 'InetAddr', using the given port number.
sock2inet :: PortNumber -> SockAddr -> InetAddr
sock2inet i (SockAddrInet _ a) = InetAddr (SockAddrInet i a)
sock2inet i (SockAddrInet6 _ f a b) = InetAddr (SockAddrInet6 i f a b)
sock2inet _ unix = InetAddr unix
-- | Map an 'IP' into an 'InetAddr', using the given port number.
ip2inet :: PortNumber -> IP -> InetAddr
ip2inet p (IPv4 a) = InetAddr $ SockAddrInet p (toHostAddress a)
ip2inet p (IPv6 a) = InetAddr $ SockAddrInet6 p 0 (toHostAddress6 a) 0
inet2ip :: InetAddr -> IP
inet2ip (InetAddr (SockAddrInet _ a)) = IPv4 (fromHostAddress a)
inet2ip (InetAddr (SockAddrInet6 _ _ a6 _)) = IPv6 (fromHostAddress6 a6)
inet2ip x = error $ "inet2ip: expecting IPv4 or IPv6, got: " ++ show x
inet2ipPort :: InetAddr -> (IP, PortNumber)
inet2ipPort (InetAddr (SockAddrInet p a)) = (IPv4 (fromHostAddress a), p)
inet2ipPort (InetAddr (SockAddrInet6 p _ a6 _)) = (IPv6 (fromHostAddress6 a6), p)
inet2ipPort x = error $ "inet2ipPort: expecting IPv4 or IPv6, got: " ++ show x