On Wed, Apr 2, 2014 at 9:34 AM, Jose A. Lopes <[email protected]> wrote:

> The configuration server listens on a Unix socket for connections from
> the node daemon.  The node daemon sends the instance parameters to the
> configuration server so they can be served through the metadata daemon
> web server to the instances which have the communication mechanism
> enabled.
>
> The configuration server reads the instance parameters and, currently,
> it extracts the instance's name and the instance's IP address on the
> instance communication NIC.  The instance's name is used for logging
> and the IP address is used to index the instance parameters, given
> that instances do not authenticate with the metadata daemon, and the
> only thing we know about them is their IP address.
>
> The configuration server also extracts the OS parameters, including
> public, private and secret, and creates an object containing those
> parameters and their visibility.
>
> The configuration is kept internally in an 'MVar' which will be shared
> with the metadata daemon web server.
>
> Signed-off-by: Jose A. Lopes <[email protected]>
> ---
>  Makefile.am                      |   1 +
>  src/Ganeti/Metad/ConfigServer.hs | 196
> +++++++++++++++++++++++++++++++++++++++
>  2 files changed, 197 insertions(+)
>  create mode 100644 src/Ganeti/Metad/ConfigServer.hs
>
> diff --git a/Makefile.am b/Makefile.am
> index 279013a..67b54fd 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -773,6 +773,7 @@ HS_LIB_SRCS = \
>         src/Ganeti/Logging.hs \
>         src/Ganeti/Logging/Lifted.hs \
>         src/Ganeti/Luxi.hs \
> +       src/Ganeti/Metad/ConfigServer.hs \
>         src/Ganeti/Metad/Types.hs \
>         src/Ganeti/Metad/WebServer.hs \
>         src/Ganeti/Monitoring/Server.hs \
> diff --git a/src/Ganeti/Metad/ConfigServer.hs
> b/src/Ganeti/Metad/ConfigServer.hs
> new file mode 100644
> index 0000000..b49c7d9
> --- /dev/null
> +++ b/src/Ganeti/Metad/ConfigServer.hs
> @@ -0,0 +1,196 @@
> +{-# LANGUAGE TupleSections #-}
> +{-| Configuration server for the metadata daemon.
> +
> +-}
> +
> +{-
> +
> +Copyright (C) 2014 Google Inc.
> +
> +This program is free software; you can redistribute it and/or modify
> +it under the terms of the GNU General Public License as published by
> +the Free Software Foundation; either version 2 of the License, or
> +(at your option) any later version.
> +
> +This program is distributed in the hope that it will be useful, but
> +WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with this program; if not, write to the Free Software
> +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> +02110-1301, USA.
> +
> +-}
> +module Ganeti.Metad.ConfigServer where
> +
> +import Control.Arrow (second)
> +import Control.Concurrent
> +import Control.Exception (try, finally)
> +import Control.Monad (unless)
> +import qualified Data.List as List
> +import qualified Data.Map as Map
> +import Text.JSON
> +import qualified Text.JSON as JSON
> +import System.FilePath ((</>))
> +import System.IO.Error (isEOFError)
> +
> +import Ganeti.Constants as Constants
> +import Ganeti.Path as Path
> +import Ganeti.Daemon (DaemonOptions)
> +import qualified Ganeti.Logging as Logging
> +import Ganeti.Runtime (GanetiDaemon(..))
> +import Ganeti.UDSServer (Client, ConnectConfig(..), Server)
> +import qualified Ganeti.UDSServer as UDSServer
> +
> +import Ganeti.Metad.Types (InstanceParams)
> +
> +metadSocket :: IO FilePath
> +metadSocket =
> +  do dir <- Path.socketDir
> +     return $ dir </> "ganeti-metad"
> +
> +mergeConfig :: InstanceParams -> InstanceParams -> InstanceParams
> +mergeConfig cfg1 cfg2 = cfg2 `Map.union` cfg1
> +
> +-- | Extracts the OS parameters (public, private, secret) from a JSON
> +-- object.
> +--
> +-- This function checks whether the OS parameters are in fact a JSON
> +-- object.
> +getOsParams :: String -> String -> JSObject JSValue -> Result (JSObject
> JSValue)
> +getOsParams key msg jsonObj =
> +  case lookup key (fromJSObject jsonObj) of
> +    Nothing -> Error $ "Could not find " ++ msg ++ " OS parameters"
> +    Just (JSObject x) -> Ok x
> +    _ -> Error "OS params is not a JSON object"
> +
> +getPublicOsParams :: JSObject JSValue -> Result (JSObject JSValue)
> +getPublicOsParams = getOsParams "osparams" "public"
> +
> +getPrivateOsParams :: JSObject JSValue -> Result (JSObject JSValue)
> +getPrivateOsParams = getOsParams "osparams_private" "private"
> +
> +getSecretOsParams :: JSObject JSValue -> Result (JSObject JSValue)
> +getSecretOsParams = getOsParams "osparams_secret" "secret"
> +
> +-- | Finds the IP address of the instance communication NIC in the
> +-- instance's NICs.
> +getInstanceCommunicationIp :: JSObject JSValue -> Result String
> +getInstanceCommunicationIp jsonObj =
> +  getNics >>= getInstanceCommunicationNic >>= getIp
> +  where
> +    getIp nic =
> +      case lookup "ip" (fromJSObject nic) of
> +        Nothing -> Error "Could not find instance communication IP"
> +        Just (JSString ip) -> Ok (JSON.fromJSString ip)
> +        _ -> Error "Instance communication IP is not a string"
> +
> +    getInstanceCommunicationNic [] =
> +      Error "Could not find instance communication NIC"
> +    getInstanceCommunicationNic (JSObject nic:nics) =
> +      case lookup "name" (fromJSObject nic) of
> +        Just (JSString name)
> +          | Constants.instanceCommunicationNicPrefix
> +            `List.isPrefixOf` JSON.fromJSString name ->
> +            Ok nic
> +        _ -> getInstanceCommunicationNic nics
> +    getInstanceCommunicationNic (_:nics) =
> +      getInstanceCommunicationNic nics
>

Why not an error here?


> +
> +    getNics =
> +      case lookup "nics" (fromJSObject jsonObj) of
> +        Nothing -> Error "Could not find OS parameters key 'nics'"
> +        Just (JSArray nics) -> Ok nics
> +        _ -> Error "Instance nics is not an array"
> +
> +-- | Merges the OS parameters (public, private, secret) in a single
> +-- data structure containing all parameters and their visibility.
> +--
> +-- Example:
> +--   { "os-image": ["http://example.com/disk.img";, "public"],
> +--     "os-password": ["mypassword", "secret"] }
> +makeInstanceParams
> +  :: JSObject JSValue -> JSObject JSValue -> JSObject JSValue -> JSValue
> +makeInstanceParams pub priv sec =
> +  JSObject . JSON.toJSObject $
> +    addVisibility "public" pub ++
> +    addVisibility "private" priv ++
> +    addVisibility "secret" sec
> +  where
> +    key = JSString . JSON.toJSString
> +
> +    addVisibility param params =
> +      map (second (JSArray . (:[key param]))) (JSON.fromJSObject params)
> +
> +-- | Extracts the OS parameters from the instance's parameters and
> +-- returns a data structure containing all the OS parameters and their
> +-- visibility indexed by the instance's IP address which is used in
> +-- the instance communication NIC.
> +getInstanceParams :: JSValue -> Result (String, InstanceParams)
> +getInstanceParams json =
> +    case json of
> +      JSObject jsonObj -> do
> +        name <- case lookup "name" (fromJSObject jsonObj) of
> +                  Nothing -> Error "Could not find instance name"
> +                  Just (JSString x) -> Ok (JSON.fromJSString x)
> +                  _ -> Error "Name is not a string"
> +        ip <- getInstanceCommunicationIp jsonObj
> +        publicOsParams <- getPublicOsParams jsonObj
> +        privateOsParams <- getPrivateOsParams jsonObj
> +        secretOsParams <- getSecretOsParams jsonObj
> +        let instanceParams =
> +              makeInstanceParams publicOsParams privateOsParams
> secretOsParams
> +        Ok (name, Map.fromList [(ip, instanceParams)])
> +      _ ->
> +        Error "Expecting a dictionary"
> +
> +-- | Update the configuration with the received instance parameters.
> +updateConfig :: MVar InstanceParams -> String -> IO ()
> +updateConfig config str =
> +  case decode str of
> +    Error err ->
> +      Logging.logDebug $ show err
> +    Ok x ->
> +      case getInstanceParams x of
> +        Error err ->
> +          Logging.logError $ "Could not get instance parameters: " ++ err
> +        Ok (name, instanceParams) -> do
> +          cfg <- takeMVar config
> +          let cfg' = mergeConfig cfg instanceParams
> +          putMVar config cfg'
> +          Logging.logInfo $
> +            "Updated instance " ++ show name ++ " configuration"
> +          Logging.logDebug $ "Instance configuration: " ++ show cfg'
> +
> +-- | Reads messages from clients and update the configuration
> +-- according to these messages.
> +acceptConfig :: MVar InstanceParams -> Client -> IO ()
> +acceptConfig config client =
> +  do res <- try $ UDSServer.recvMsg client
> +     case res of
> +       Left err -> do
> +         unless (isEOFError err) .
> +           Logging.logDebug $ show err
> +         return ()
> +       Right str -> do
> +         Logging.logDebug $ "Received: " ++ str
> +         updateConfig config str
> +
> +-- | Loop that accepts clients and dispatches them to an isolated
> +-- thread that will handle the client's requests.
> +acceptClients :: MVar InstanceParams -> Server -> IO ()
> +acceptClients config server =
> +  do client <- UDSServer.acceptClient server
> +     _ <- forkIO $ acceptConfig config client
> +     acceptClients config server
> +
> +start :: DaemonOptions -> MVar InstanceParams -> IO ()
> +start _ config =
> +  do server <- UDSServer.connectServer metadConfig True =<< metadSocket
> +     finally
> +       (acceptClients config server)
> +       (UDSServer.closeServer server)
> +  where
> +    metadConfig = ConnectConfig GanetiMetad 60 60
> --
> 1.9.1.423.g4596e3a
>
>

Reply via email to