On Wed, Apr 2, 2014 at 11:49 AM, Jose A. Lopes <[email protected]> wrote:
> 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? > > We are looking for an object containing the "name" attribute with a > special value. Everything else can be skipped. We could raise an > error, but this way the code is more resilient to crap inside the > data. > > Ok, but please add a warning so that we have some trail of what went wrong if something goes wrong. > > > > > + > > > + 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 >
