Statistics
| Branch: | Tag: | Revision:

root / lib / ssconf.py @ a0c26bdb

History | View | Annotate | Download (9.1 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.Reload()
56

    
57
  def Reload(self):
58
    """Reloads the config file.
59

60
    """
61
    try:
62
      self._config_data = serializer.Load(utils.ReadFile(self._file_name))
63
    except IOError, err:
64
      raise errors.ConfigurationError("Cannot read config file %s: %s" %
65
                                      (self._file_name, err))
66
    except ValueError, err:
67
      raise errors.ConfigurationError("Cannot load config file %s: %s" %
68
                                      (self._file_name, err))
69

    
70
  def GetClusterName(self):
71
    return self._config_data["cluster"]["cluster_name"]
72

    
73
  def GetHostKey(self):
74
    return self._config_data["cluster"]["rsahostkeypub"]
75

    
76
  def GetMasterNode(self):
77
    return self._config_data["cluster"]["master_node"]
78

    
79
  def GetMasterIP(self):
80
    return self._config_data["cluster"]["master_ip"]
81

    
82
  def GetMasterNetdev(self):
83
    return self._config_data["cluster"]["master_netdev"]
84

    
85
  def GetFileStorageDir(self):
86
    return self._config_data["cluster"]["file_storage_dir"]
87

    
88
  def GetHypervisorType(self):
89
    return self._config_data["cluster"]["hypervisor"]
90

    
91
  def GetNodeList(self):
92
    return self._config_data["nodes"].keys()
93

    
94
  def GetConfigSerialNo(self):
95
    return self._config_data["serial_no"]
96

    
97
  def GetClusterSerialNo(self):
98
    return self._config_data["cluster"]["serial_no"]
99

    
100

    
101
class SimpleStore(object):
102
  """Interface to static cluster data.
103

104
  This is different that the config.ConfigWriter and
105
  SimpleConfigReader classes in that it holds data that will always be
106
  present, even on nodes which don't have all the cluster data.
107

108
  Other particularities of the datastore:
109
    - keys are restricted to predefined values
110

111
  """
112
  _SS_FILEPREFIX = "ssconf_"
113
  _VALID_KEYS = (
114
    constants.SS_CLUSTER_NAME,
115
    constants.SS_CLUSTER_TAGS,
116
    constants.SS_FILE_STORAGE_DIR,
117
    constants.SS_MASTER_CANDIDATES,
118
    constants.SS_MASTER_CANDIDATES_IPS,
119
    constants.SS_MASTER_IP,
120
    constants.SS_MASTER_NETDEV,
121
    constants.SS_MASTER_NODE,
122
    constants.SS_NODE_LIST,
123
    constants.SS_NODE_PRIMARY_IPS,
124
    constants.SS_NODE_SECONDARY_IPS,
125
    constants.SS_OFFLINE_NODES,
126
    constants.SS_ONLINE_NODES,
127
    constants.SS_INSTANCE_LIST,
128
    constants.SS_RELEASE_VERSION,
129
    )
130
  _MAX_SIZE = 131072
131

    
132
  def __init__(self, cfg_location=None):
133
    if cfg_location is None:
134
      self._cfg_dir = constants.DATA_DIR
135
    else:
136
      self._cfg_dir = cfg_location
137

    
138
  def KeyToFilename(self, key):
139
    """Convert a given key into filename.
140

141
    """
142
    if key not in self._VALID_KEYS:
143
      raise errors.ProgrammerError("Invalid key requested from SSConf: '%s'"
144
                                   % str(key))
145

    
146
    filename = self._cfg_dir + '/' + self._SS_FILEPREFIX + key
147
    return filename
148

    
149
  def _ReadFile(self, key):
150
    """Generic routine to read keys.
151

152
    This will read the file which holds the value requested. Errors
153
    will be changed into ConfigurationErrors.
154

155
    """
156
    filename = self.KeyToFilename(key)
157
    try:
158
      data = utils.ReadFile(filename, size=self._MAX_SIZE)
159
    except EnvironmentError, err:
160
      raise errors.ConfigurationError("Can't read from the ssconf file:"
161
                                      " '%s'" % str(err))
162
    data = data.rstrip('\n')
163
    return data
164

    
165
  def WriteFiles(self, values):
166
    """Writes ssconf files used by external scripts.
167

168
    @type values: dict
169
    @param values: Dictionary of (name, value)
170

171
    """
172
    ssconf_lock = utils.FileLock(constants.SSCONF_LOCK_FILE)
173

    
174
    # Get lock while writing files
175
    ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT)
176
    try:
177
      for name, value in values.iteritems():
178
        if value and not value.endswith("\n"):
179
          value += "\n"
180
        utils.WriteFile(self.KeyToFilename(name), data=value, mode=0444)
181
    finally:
182
      ssconf_lock.Unlock()
183

    
184
  def GetFileList(self):
185
    """Return the list of all config files.
186

187
    This is used for computing node replication data.
188

189
    """
190
    return [self.KeyToFilename(key) for key in self._VALID_KEYS]
191

    
192
  def GetClusterName(self):
193
    """Get the cluster name.
194

195
    """
196
    return self._ReadFile(constants.SS_CLUSTER_NAME)
197

    
198
  def GetFileStorageDir(self):
199
    """Get the file storage dir.
200

201
    """
202
    return self._ReadFile(constants.SS_FILE_STORAGE_DIR)
203

    
204
  def GetMasterCandidates(self):
205
    """Return the list of master candidates.
206

207
    """
208
    data = self._ReadFile(constants.SS_MASTER_CANDIDATES)
209
    nl = data.splitlines(False)
210
    return nl
211

    
212
  def GetMasterCandidatesIPList(self):
213
    """Return the list of master candidates' primary IP.
214

215
    """
216
    data = self._ReadFile(constants.SS_MASTER_CANDIDATES_IPS)
217
    nl = data.splitlines(False)
218
    return nl
219

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

223
    """
224
    return self._ReadFile(constants.SS_MASTER_IP)
225

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

229
    """
230
    return self._ReadFile(constants.SS_MASTER_NETDEV)
231

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

235
    """
236
    return self._ReadFile(constants.SS_MASTER_NODE)
237

    
238
  def GetNodeList(self):
239
    """Return the list of cluster nodes.
240

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

    
246
  def GetNodePrimaryIPList(self):
247
    """Return the list of cluster nodes' primary IP.
248

249
    """
250
    data = self._ReadFile(constants.SS_NODE_PRIMARY_IPS)
251
    nl = data.splitlines(False)
252
    return nl
253

    
254
  def GetNodeSecondaryIPList(self):
255
    """Return the list of cluster nodes' secondary IP.
256

257
    """
258
    data = self._ReadFile(constants.SS_NODE_SECONDARY_IPS)
259
    nl = data.splitlines(False)
260
    return nl
261

    
262
  def GetClusterTags(self):
263
    """Return the cluster tags.
264

265
    """
266
    data = self._ReadFile(constants.SS_CLUSTER_TAGS)
267
    nl = data.splitlines(False)
268
    return nl
269

    
270

    
271
def GetMasterAndMyself(ss=None):
272
  """Get the master node and my own hostname.
273

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

277
  The function does not handle any errors, these should be handled in
278
  the caller (errors.ConfigurationError, errors.ResolverError).
279

280
  @param ss: either a sstore.SimpleConfigReader or a
281
      sstore.SimpleStore instance
282
  @rtype: tuple
283
  @return: a tuple (master node name, my own name)
284

285
  """
286
  if ss is None:
287
    ss = SimpleStore()
288
  return ss.GetMasterNode(), utils.HostInfo().name
289

    
290

    
291
def CheckMaster(debug, ss=None):
292
  """Checks the node setup.
293

294
  If this is the master, the function will return. Otherwise it will
295
  exit with an exit code based on the node status.
296

297
  """
298
  try:
299
    master_name, myself = GetMasterAndMyself(ss)
300
  except errors.ConfigurationError, err:
301
    print "Cluster configuration incomplete: '%s'" % str(err)
302
    sys.exit(constants.EXIT_NODESETUP_ERROR)
303
  except errors.ResolverError, err:
304
    sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0])
305
    sys.exit(constants.EXIT_NODESETUP_ERROR)
306

    
307
  if myself != master_name:
308
    if debug:
309
      sys.stderr.write("Not master, exiting.\n")
310
    sys.exit(constants.EXIT_NOTMASTER)
311

    
312

    
313
def CheckMasterCandidate(debug, ss=None):
314
  """Checks the node setup.
315

316
  If this is a master candidate, the function will return. Otherwise it will
317
  exit with an exit code based on the node status.
318

319
  """
320
  try:
321
    if ss is None:
322
      ss = SimpleStore()
323
    myself = utils.HostInfo().name
324
    candidates = ss.GetMasterCandidates()
325
  except errors.ConfigurationError, err:
326
    print "Cluster configuration incomplete: '%s'" % str(err)
327
    sys.exit(constants.EXIT_NODESETUP_ERROR)
328
  except errors.ResolverError, err:
329
    sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0])
330
    sys.exit(constants.EXIT_NODESETUP_ERROR)
331

    
332
  if myself not in candidates:
333
    if debug:
334
      sys.stderr.write("Not master candidate, exiting.\n")
335
    sys.exit(constants.EXIT_NOTCANDIDATE)
336