Statistics
| Branch: | Tag: | Revision:

root / lib / ssconf.py @ 8135a2db

History | View | Annotate | Download (7.6 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Global Configuration data for Ganeti.
23

24
This module provides the interface to a special case of cluster
25
configuration data, which is mostly static and available to all nodes.
26

27
"""
28

    
29
import sys
30
import re
31

    
32
from ganeti import errors
33
from ganeti import constants
34
from ganeti import utils
35
from ganeti import serializer
36

    
37

    
38
SSCONF_LOCK_TIMEOUT = 10
39

    
40
RE_VALID_SSCONF_NAME = re.compile(r'^[-_a-z0-9]+$')
41

    
42

    
43
class SimpleConfigReader(object):
44
  """Simple class to read configuration file.
45

46
  """
47
  def __init__(self, file_name=constants.CLUSTER_CONF_FILE):
48
    """Initializes this class.
49

50
    @type file_name: string
51
    @param file_name: Configuration file path
52

53
    """
54
    self._file_name = file_name
55
    self._config_data = serializer.Load(utils.ReadFile(file_name))
56
    # TODO: Error handling
57

    
58
  def GetClusterName(self):
59
    return self._config_data["cluster"]["cluster_name"]
60

    
61
  def GetHostKey(self):
62
    return self._config_data["cluster"]["rsahostkeypub"]
63

    
64
  def GetMasterNode(self):
65
    return self._config_data["cluster"]["master_node"]
66

    
67
  def GetMasterIP(self):
68
    return self._config_data["cluster"]["master_ip"]
69

    
70
  def GetMasterNetdev(self):
71
    return self._config_data["cluster"]["master_netdev"]
72

    
73
  def GetFileStorageDir(self):
74
    return self._config_data["cluster"]["file_storage_dir"]
75

    
76
  def GetHypervisorType(self):
77
    return self._config_data["cluster"]["hypervisor"]
78

    
79
  def GetNodeList(self):
80
    return self._config_data["nodes"].keys()
81

    
82
  @classmethod
83
  def FromDict(cls, val, cfg_file=constants.CLUSTER_CONF_FILE):
84
    """Alternative construction from a dictionary.
85

86
    """
87
    obj = SimpleConfigReader.__new__(cls)
88
    obj._config_data = val
89
    obj._file_name = cfg_file
90
    return obj
91

    
92

    
93
class SimpleConfigWriter(SimpleConfigReader):
94
  """Simple class to write configuration file.
95

96
  """
97
  def SetMasterNode(self, node):
98
    """Change master node.
99

100
    """
101
    self._config_data["cluster"]["master_node"] = node
102

    
103
  def Save(self):
104
    """Writes configuration file.
105

106
    Warning: Doesn't take care of locking or synchronizing with other
107
    processes.
108

109
    """
110
    utils.WriteFile(self._file_name,
111
                    data=serializer.Dump(self._config_data),
112
                    mode=0600)
113

    
114

    
115
class SimpleStore(object):
116
  """Interface to static cluster data.
117

118
  This is different that the config.ConfigWriter and
119
  SimpleConfigReader classes in that it holds data that will always be
120
  present, even on nodes which don't have all the cluster data.
121

122
  Other particularities of the datastore:
123
    - keys are restricted to predefined values
124

125
  """
126
  _SS_FILEPREFIX = "ssconf_"
127
  _VALID_KEYS = (
128
    constants.SS_CLUSTER_NAME,
129
    constants.SS_FILE_STORAGE_DIR,
130
    constants.SS_MASTER_CANDIDATES,
131
    constants.SS_MASTER_IP,
132
    constants.SS_MASTER_NETDEV,
133
    constants.SS_MASTER_NODE,
134
    constants.SS_NODE_LIST,
135
    )
136
  _MAX_SIZE = 131072
137

    
138
  def __init__(self, cfg_location=None):
139
    if cfg_location is None:
140
      self._cfg_dir = constants.DATA_DIR
141
    else:
142
      self._cfg_dir = cfg_location
143

    
144
  def KeyToFilename(self, key):
145
    """Convert a given key into filename.
146

147
    """
148
    if key not in self._VALID_KEYS:
149
      raise errors.ProgrammerError("Invalid key requested from SSConf: '%s'"
150
                                   % str(key))
151

    
152
    filename = self._cfg_dir + '/' + self._SS_FILEPREFIX + key
153
    return filename
154

    
155
  def _ReadFile(self, key):
156
    """Generic routine to read keys.
157

158
    This will read the file which holds the value requested. Errors
159
    will be changed into ConfigurationErrors.
160

161
    """
162
    filename = self.KeyToFilename(key)
163
    try:
164
      fh = file(filename, 'r')
165
      try:
166
        data = fh.read(self._MAX_SIZE)
167
        data = data.rstrip('\n')
168
      finally:
169
        fh.close()
170
    except EnvironmentError, err:
171
      raise errors.ConfigurationError("Can't read from the ssconf file:"
172
                                      " '%s'" % str(err))
173
    return data
174

    
175
  def WriteFiles(self, values):
176
    """Writes ssconf files used by external scripts.
177

178
    @type values: dict
179
    @param values: Dictionary of (name, value)
180

181
    """
182
    ssconf_lock = utils.FileLock(constants.SSCONF_LOCK_FILE)
183

    
184
    # Get lock while writing files
185
    ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT)
186
    try:
187
      for name, value in values.iteritems():
188
        if not value.endswith("\n"):
189
          value += "\n"
190
        utils.WriteFile(self.KeyToFilename(name), data=value)
191
    finally:
192
      ssconf_lock.Unlock()
193

    
194
  def GetFileList(self):
195
    """Return the list of all config files.
196

197
    This is used for computing node replication data.
198

199
    """
200
    return [self.KeyToFilename(key) for key in self._VALID_KEYS]
201

    
202
  def GetClusterName(self):
203
    """Get the cluster name.
204

205
    """
206
    return self._ReadFile(constants.SS_CLUSTER_NAME)
207

    
208
  def GetFileStorageDir(self):
209
    """Get the file storage dir.
210

211
    """
212
    return self._ReadFile(constants.SS_FILE_STORAGE_DIR)
213

    
214
  def GetMasterCandidates(self):
215
    """Return the list of master candidates.
216

217
    """
218
    data = self._ReadFile(constants.SS_MASTER_CANDIDATES)
219
    nl = data.splitlines(False)
220
    return nl
221

    
222
  def GetMasterIP(self):
223
    """Get the IP of the master node for this cluster.
224

225
    """
226
    return self._ReadFile(constants.SS_MASTER_IP)
227

    
228
  def GetMasterNetdev(self):
229
    """Get the netdev to which we'll add the master ip.
230

231
    """
232
    return self._ReadFile(constants.SS_MASTER_NETDEV)
233

    
234
  def GetMasterNode(self):
235
    """Get the hostname of the master node for this cluster.
236

237
    """
238
    return self._ReadFile(constants.SS_MASTER_NODE)
239

    
240
  def GetNodeList(self):
241
    """Return the list of cluster nodes.
242

243
    """
244
    data = self._ReadFile(constants.SS_NODE_LIST)
245
    nl = data.splitlines(False)
246
    return nl
247

    
248

    
249
def GetMasterAndMyself(ss=None):
250
  """Get the master node and my own hostname.
251

252
  This can be either used for a 'soft' check (compared to CheckMaster,
253
  which exits) or just for computing both at the same time.
254

255
  The function does not handle any errors, these should be handled in
256
  the caller (errors.ConfigurationError, errors.ResolverError).
257

258
  @param ss: either a sstore.SimpleConfigReader or a
259
      sstore.SimpleStore instance
260
  @rtype: tuple
261
  @return: a tuple (master node name, my own name)
262

263
  """
264
  if ss is None:
265
    ss = SimpleStore()
266
  return ss.GetMasterNode(), utils.HostInfo().name
267

    
268

    
269
def CheckMaster(debug, ss=None):
270
  """Checks the node setup.
271

272
  If this is the master, the function will return. Otherwise it will
273
  exit with an exit code based on the node status.
274

275
  """
276
  try:
277
    master_name, myself = GetMasterAndMyself(ss)
278
  except errors.ConfigurationError, err:
279
    print "Cluster configuration incomplete: '%s'" % str(err)
280
    sys.exit(constants.EXIT_NODESETUP_ERROR)
281
  except errors.ResolverError, err:
282
    sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0])
283
    sys.exit(constants.EXIT_NODESETUP_ERROR)
284

    
285
  if myself != master_name:
286
    if debug:
287
      sys.stderr.write("Not master, exiting.\n")
288
    sys.exit(constants.EXIT_NOTMASTER)