Statistics
| Branch: | Tag: | Revision:

root / lib / ssconf.py @ 1fe93c75

History | View | Annotate | Download (9 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

    
95
class SimpleStore(object):
96
  """Interface to static cluster data.
97

98
  This is different that the config.ConfigWriter and
99
  SimpleConfigReader classes in that it holds data that will always be
100
  present, even on nodes which don't have all the cluster data.
101

102
  Other particularities of the datastore:
103
    - keys are restricted to predefined values
104

105
  """
106
  _SS_FILEPREFIX = "ssconf_"
107
  _VALID_KEYS = (
108
    constants.SS_CLUSTER_NAME,
109
    constants.SS_CLUSTER_TAGS,
110
    constants.SS_FILE_STORAGE_DIR,
111
    constants.SS_MASTER_CANDIDATES,
112
    constants.SS_MASTER_CANDIDATES_IPS,
113
    constants.SS_MASTER_IP,
114
    constants.SS_MASTER_NETDEV,
115
    constants.SS_MASTER_NODE,
116
    constants.SS_NODE_LIST,
117
    constants.SS_NODE_PRIMARY_IPS,
118
    constants.SS_NODE_SECONDARY_IPS,
119
    constants.SS_OFFLINE_NODES,
120
    constants.SS_ONLINE_NODES,
121
    constants.SS_INSTANCE_LIST,
122
    constants.SS_RELEASE_VERSION,
123
    )
124
  _MAX_SIZE = 131072
125

    
126
  def __init__(self, cfg_location=None):
127
    if cfg_location is None:
128
      self._cfg_dir = constants.DATA_DIR
129
    else:
130
      self._cfg_dir = cfg_location
131

    
132
  def KeyToFilename(self, key):
133
    """Convert a given key into filename.
134

135
    """
136
    if key not in self._VALID_KEYS:
137
      raise errors.ProgrammerError("Invalid key requested from SSConf: '%s'"
138
                                   % str(key))
139

    
140
    filename = self._cfg_dir + '/' + self._SS_FILEPREFIX + key
141
    return filename
142

    
143
  def _ReadFile(self, key):
144
    """Generic routine to read keys.
145

146
    This will read the file which holds the value requested. Errors
147
    will be changed into ConfigurationErrors.
148

149
    """
150
    filename = self.KeyToFilename(key)
151
    try:
152
      data = utils.ReadFile(filename, size=self._MAX_SIZE)
153
    except EnvironmentError, err:
154
      raise errors.ConfigurationError("Can't read from the ssconf file:"
155
                                      " '%s'" % str(err))
156
    data = data.rstrip('\n')
157
    return data
158

    
159
  def WriteFiles(self, values):
160
    """Writes ssconf files used by external scripts.
161

162
    @type values: dict
163
    @param values: Dictionary of (name, value)
164

165
    """
166
    ssconf_lock = utils.FileLock(constants.SSCONF_LOCK_FILE)
167

    
168
    # Get lock while writing files
169
    ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT)
170
    try:
171
      for name, value in values.iteritems():
172
        if value and not value.endswith("\n"):
173
          value += "\n"
174
        utils.WriteFile(self.KeyToFilename(name), data=value, mode=0444)
175
    finally:
176
      ssconf_lock.Unlock()
177

    
178
  def GetFileList(self):
179
    """Return the list of all config files.
180

181
    This is used for computing node replication data.
182

183
    """
184
    return [self.KeyToFilename(key) for key in self._VALID_KEYS]
185

    
186
  def GetClusterName(self):
187
    """Get the cluster name.
188

189
    """
190
    return self._ReadFile(constants.SS_CLUSTER_NAME)
191

    
192
  def GetFileStorageDir(self):
193
    """Get the file storage dir.
194

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

    
198
  def GetMasterCandidates(self):
199
    """Return the list of master candidates.
200

201
    """
202
    data = self._ReadFile(constants.SS_MASTER_CANDIDATES)
203
    nl = data.splitlines(False)
204
    return nl
205

    
206
  def GetMasterCandidatesIPList(self):
207
    """Return the list of master candidates' primary IP.
208

209
    """
210
    data = self._ReadFile(constants.SS_MASTER_CANDIDATES_IPS)
211
    nl = data.splitlines(False)
212
    return nl
213

    
214
  def GetMasterIP(self):
215
    """Get the IP of the master node for this cluster.
216

217
    """
218
    return self._ReadFile(constants.SS_MASTER_IP)
219

    
220
  def GetMasterNetdev(self):
221
    """Get the netdev to which we'll add the master ip.
222

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

    
226
  def GetMasterNode(self):
227
    """Get the hostname of the master node for this cluster.
228

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

    
232
  def GetNodeList(self):
233
    """Return the list of cluster nodes.
234

235
    """
236
    data = self._ReadFile(constants.SS_NODE_LIST)
237
    nl = data.splitlines(False)
238
    return nl
239

    
240
  def GetNodePrimaryIPList(self):
241
    """Return the list of cluster nodes' primary IP.
242

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

    
248
  def GetNodeSecondaryIPList(self):
249
    """Return the list of cluster nodes' secondary IP.
250

251
    """
252
    data = self._ReadFile(constants.SS_NODE_SECONDARY_IPS)
253
    nl = data.splitlines(False)
254
    return nl
255

    
256
  def GetClusterTags(self):
257
    """Return the cluster tags.
258

259
    """
260
    data = self._ReadFile(constants.SS_CLUSTER_TAGS)
261
    nl = data.splitlines(False)
262
    return nl
263

    
264

    
265
def GetMasterAndMyself(ss=None):
266
  """Get the master node and my own hostname.
267

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

271
  The function does not handle any errors, these should be handled in
272
  the caller (errors.ConfigurationError, errors.ResolverError).
273

274
  @param ss: either a sstore.SimpleConfigReader or a
275
      sstore.SimpleStore instance
276
  @rtype: tuple
277
  @return: a tuple (master node name, my own name)
278

279
  """
280
  if ss is None:
281
    ss = SimpleStore()
282
  return ss.GetMasterNode(), utils.HostInfo().name
283

    
284

    
285
def CheckMaster(debug, ss=None):
286
  """Checks the node setup.
287

288
  If this is the master, the function will return. Otherwise it will
289
  exit with an exit code based on the node status.
290

291
  """
292
  try:
293
    master_name, myself = GetMasterAndMyself(ss)
294
  except errors.ConfigurationError, err:
295
    print "Cluster configuration incomplete: '%s'" % str(err)
296
    sys.exit(constants.EXIT_NODESETUP_ERROR)
297
  except errors.ResolverError, err:
298
    sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0])
299
    sys.exit(constants.EXIT_NODESETUP_ERROR)
300

    
301
  if myself != master_name:
302
    if debug:
303
      sys.stderr.write("Not master, exiting.\n")
304
    sys.exit(constants.EXIT_NOTMASTER)
305

    
306

    
307
def CheckMasterCandidate(debug, ss=None):
308
  """Checks the node setup.
309

310
  If this is a master candidate, the function will return. Otherwise it will
311
  exit with an exit code based on the node status.
312

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

    
326
  if myself not in candidates:
327
    if debug:
328
      sys.stderr.write("Not master candidate, exiting.\n")
329
    sys.exit(constants.EXIT_NOTCANDIDATE)
330