Ssconf.hs 4.82 KB
Newer Older
Iustin Pop's avatar
Iustin Pop committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{-# LANGUAGE TemplateHaskell #-}

{-| Implementation of the Ganeti Ssconf interface.

-}

{-

Copyright (C) 2012 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.Ssconf
  ( SSKey(..)
  , sSKeyToRaw
  , sSKeyFromRaw
  , getPrimaryIPFamily
33
  , getMasterCandidatesIps
34
35
  , keyToFilename
  , sSFilePrefix
Iustin Pop's avatar
Iustin Pop committed
36
37
38
39
  ) where

import Ganeti.THH

40
import Control.Exception
Iustin Pop's avatar
Iustin Pop committed
41
42
43
44
import Control.Monad (liftM)
import Data.Maybe (fromMaybe)
import qualified Network.Socket as Socket
import System.FilePath ((</>))
45
import System.IO.Error (isDoesNotExistError)
Iustin Pop's avatar
Iustin Pop committed
46
47

import qualified Ganeti.Constants as C
48
import qualified Ganeti.Path as Path
Iustin Pop's avatar
Iustin Pop committed
49
import Ganeti.BasicTypes
50
import Ganeti.Utils
Iustin Pop's avatar
Iustin Pop committed
51
52
53
54
55

-- | Maximum ssconf file size we support.
maxFileSize :: Int
maxFileSize = 131072

56
57
58
59
-- | ssconf file prefix, re-exported from Constants.
sSFilePrefix :: FilePath
sSFilePrefix = C.ssconfFileprefix

Iustin Pop's avatar
Iustin Pop committed
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
$(declareSADT "SSKey"
  [ ("SSClusterName",          'C.ssClusterName)
  , ("SSClusterTags",          'C.ssClusterTags)
  , ("SSFileStorageDir",       'C.ssFileStorageDir)
  , ("SSSharedFileStorageDir", 'C.ssSharedFileStorageDir)
  , ("SSMasterCandidates",     'C.ssMasterCandidates)
  , ("SSMasterCandidatesIps",  'C.ssMasterCandidatesIps)
  , ("SSMasterIp",             'C.ssMasterIp)
  , ("SSMasterNetdev",         'C.ssMasterNetdev)
  , ("SSMasterNetmask",        'C.ssMasterNetmask)
  , ("SSMasterNode",           'C.ssMasterNode)
  , ("SSNodeList",             'C.ssNodeList)
  , ("SSNodePrimaryIps",       'C.ssNodePrimaryIps)
  , ("SSNodeSecondaryIps",     'C.ssNodeSecondaryIps)
  , ("SSOfflineNodes",         'C.ssOfflineNodes)
  , ("SSOnlineNodes",          'C.ssOnlineNodes)
  , ("SSPrimaryIpFamily",      'C.ssPrimaryIpFamily)
  , ("SSInstanceList",         'C.ssInstanceList)
  , ("SSReleaseVersion",       'C.ssReleaseVersion)
  , ("SSHypervisorList",       'C.ssHypervisorList)
  , ("SSMaintainNodeHealth",   'C.ssMaintainNodeHealth)
  , ("SSUidPool",              'C.ssUidPool)
  , ("SSNodegroups",           'C.ssNodegroups)
  ])

-- | Convert a ssconf key into a (full) file path.
86
87
88
keyToFilename :: FilePath     -- ^ Config path root
              -> SSKey        -- ^ Ssconf key
              -> FilePath     -- ^ Full file name
Iustin Pop's avatar
Iustin Pop committed
89
keyToFilename cfgpath key =
90
  cfgpath </> sSFilePrefix ++ sSKeyToRaw key
Iustin Pop's avatar
Iustin Pop committed
91
92
93
94
95
96
97
98

-- | Runs an IO action while transforming any error into 'Bad'
-- values. It also accepts an optional value to use in case the error
-- is just does not exist.
catchIOErrors :: Maybe a         -- ^ Optional default
              -> IO a            -- ^ Action to run
              -> IO (Result a)
catchIOErrors def action =
99
100
  Control.Exception.catch
        (do
Iustin Pop's avatar
Iustin Pop committed
101
102
103
104
105
106
107
108
109
110
111
112
113
          result <- action
          return (Ok result)
        ) (\err -> let bad_result = Bad (show err)
                   in return $ if isDoesNotExistError err
                                 then maybe bad_result Ok def
                                 else bad_result)

-- | Read an ssconf file.
readSSConfFile :: Maybe FilePath            -- ^ Optional config path override
               -> Maybe String              -- ^ Optional default value
               -> SSKey                     -- ^ Desired ssconf key
               -> IO (Result String)
readSSConfFile optpath def key = do
Iustin Pop's avatar
Iustin Pop committed
114
  dpath <- Path.dataDir
115
  result <- catchIOErrors def . readFile .
Iustin Pop's avatar
Iustin Pop committed
116
            keyToFilename (fromMaybe dpath optpath) $ key
Iustin Pop's avatar
Iustin Pop committed
117
118
119
120
121
122
123
124
125
126
127
128
  return (liftM (take maxFileSize) result)

-- | Parses a string containing an IP family
parseIPFamily :: Int -> Result Socket.Family
parseIPFamily fam | fam == C.ip4Family = Ok Socket.AF_INET
                  | fam == C.ip6Family = Ok Socket.AF_INET6
                  | otherwise = Bad $ "Unknown af_family value: " ++ show fam

-- | Read the primary IP family.
getPrimaryIPFamily :: Maybe FilePath -> IO (Result Socket.Family)
getPrimaryIPFamily optpath = do
  result <- readSSConfFile optpath (Just (show C.ip4Family)) SSPrimaryIpFamily
129
  return (liftM rStripSpace result >>=
Iustin Pop's avatar
Iustin Pop committed
130
          tryRead "Parsing af_family" >>= parseIPFamily)
131
132
133
134
135
136

-- | Read the list of IP addresses of the master candidates of the cluster.
getMasterCandidatesIps :: Maybe FilePath -> IO (Result [String])
getMasterCandidatesIps optPath = do
  result <- readSSConfFile optPath Nothing SSMasterCandidatesIps
  return $ liftM lines result