LGTM, thanks

On Wed, Apr 2, 2014 at 12:48 PM, Jose A. Lopes <[email protected]> wrote:

> Interdiff:
>
> diff --git a/src/Ganeti/Metad/ConfigServer.hs
> b/src/Ganeti/Metad/ConfigServer.hs
> index b49c7d9..7707a16 100644
> --- a/src/Ganeti/Metad/ConfigServer.hs
> +++ b/src/Ganeti/Metad/ConfigServer.hs
> @@ -96,8 +96,8 @@ getInstanceCommunicationIp jsonObj =
>              `List.isPrefixOf` JSON.fromJSString name ->
>              Ok nic
>          _ -> getInstanceCommunicationNic nics
> -    getInstanceCommunicationNic (_:nics) =
> -      getInstanceCommunicationNic nics
> +    getInstanceCommunicationNic _ =
> +      Error "Found wrong data in instance NICs"
>
>      getNics =
>        case lookup "nics" (fromJSObject jsonObj) of
>
> Cheers,
> Jose
>
> On Apr 02 11:46, Hrvoje Ribicic wrote:
> > 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
> > >
> > >
>
> --
> Jose Antonio Lopes
> Ganeti Engineering
> Google Germany GmbH
> Dienerstr. 12, 80331, München
>
> Registergericht und -nummer: Hamburg, HRB 86891
> Sitz der Gesellschaft: Hamburg
> Geschäftsführer: Graham Law, Christine Elizabeth Flores
> Steuernummer: 48/725/00206
> Umsatzsteueridentifikationsnummer: DE813741370
>

Reply via email to