Statistics
| Branch: | Tag: | Revision:

root / lib / ssconf.py @ 01cf7dbe

History | View | Annotate | Download (8.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
    try:
56
      self._config_data = serializer.Load(utils.ReadFile(file_name))
57
    except IOError, err:
58
      raise errors.ConfigurationError("Cannot read config file %s: %s" %
59
                                      (file_name, err))
60
    except ValueError, err:
61
      raise errors.ConfigurationError("Cannot load config file %s: %s" %
62
                                      (file_name, err))
63

    
64
  def GetClusterName(self):
65
    return self._config_data["cluster"]["cluster_name"]
66

    
67
  def GetHostKey(self):
68
    return self._config_data["cluster"]["rsahostkeypub"]
69

    
70
  def GetMasterNode(self):
71
    return self._config_data["cluster"]["master_node"]
72

    
73
  def GetMasterIP(self):
74
    return self._config_data["cluster"]["master_ip"]
75

    
76
  def GetMasterNetdev(self):
77
    return self._config_data["cluster"]["master_netdev"]
78

    
79
  def GetFileStorageDir(self):
80
    return self._config_data["cluster"]["file_storage_dir"]
81

    
82
  def GetHypervisorType(self):
83
    return self._config_data["cluster"]["hypervisor"]
84

    
85
  def GetNodeList(self):
86
    return self._config_data["nodes"].keys()
87

    
88

    
89
class SimpleStore(object):
90
  """Interface to static cluster data.
91

92
  This is different that the config.ConfigWriter and
93
  SimpleConfigReader classes in that it holds data that will always be
94
  present, even on nodes which don't have all the cluster data.
95

96
  Other particularities of the datastore:
97
    - keys are restricted to predefined values
98

99
  """
100
  _SS_FILEPREFIX = "ssconf_"
101
  _VALID_KEYS = (
102
    constants.SS_CLUSTER_NAME,
103
    constants.SS_CLUSTER_TAGS,
104
    constants.SS_FILE_STORAGE_DIR,
105
    constants.SS_MASTER_CANDIDATES,
106
    constants.SS_MASTER_CANDIDATES_IPS,
107
    constants.SS_MASTER_IP,
108
    constants.SS_MASTER_NETDEV,
109
    constants.SS_MASTER_NODE,
110
    constants.SS_NODE_LIST,
111
    constants.SS_NODE_PRIMARY_IPS,
112
    constants.SS_NODE_SECONDARY_IPS,
113
    constants.SS_OFFLINE_NODES,
114
    constants.SS_ONLINE_NODES,
115
    constants.SS_INSTANCE_LIST,
116
    constants.SS_RELEASE_VERSION,
117
    )
118
  _MAX_SIZE = 131072
119

    
120
  def __init__(self, cfg_location=None):
121
    if cfg_location is None:
122
      self._cfg_dir = constants.DATA_DIR
123
    else:
124
      self._cfg_dir = cfg_location
125

    
126
  def KeyToFilename(self, key):
127
    """Convert a given key into filename.
128

129
    """
130
    if key not in self._VALID_KEYS:
131
      raise errors.ProgrammerError("Invalid key requested from SSConf: '%s'"
132
                                   % str(key))
133

    
134
    filename = self._cfg_dir + '/' + self._SS_FILEPREFIX + key
135
    return filename
136

    
137
  def _ReadFile(self, key):
138
    """Generic routine to read keys.
139

140
    This will read the file which holds the value requested. Errors
141
    will be changed into ConfigurationErrors.
142

143
    """
144
    filename = self.KeyToFilename(key)
145
    try:
146
      data = utils.ReadFile(filename, size=self._MAX_SIZE)
147
    except EnvironmentError, err:
148
      raise errors.ConfigurationError("Can't read from the ssconf file:"
149
                                      " '%s'" % str(err))
150
    data = data.rstrip('\n')
151
    return data
152

    
153
  def WriteFiles(self, values):
154
    """Writes ssconf files used by external scripts.
155

156
    @type values: dict
157
    @param values: Dictionary of (name, value)
158

159
    """
160
    ssconf_lock = utils.FileLock(constants.SSCONF_LOCK_FILE)
161

    
162
    # Get lock while writing files
163
    ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT)
164
    try:
165
      for name, value in values.iteritems():
166
        if value and not value.endswith("\n"):
167
          value += "\n"
168
        utils.WriteFile(self.KeyToFilename(name), data=value, mode=0444)
169
    finally:
170
      ssconf_lock.Unlock()
171

    
172
  def GetFileList(self):
173
    """Return the list of all config files.
174

175
    This is used for computing node replication data.
176

177
    """
178
    return [self.KeyToFilename(key) for key in self._VALID_KEYS]
179

    
180
  def GetClusterName(self):
181
    """Get the cluster name.
182

183
    """
184
    return self._ReadFile(constants.SS_CLUSTER_NAME)
185

    
186
  def GetFileStorageDir(self):
187
    """Get the file storage dir.
188

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

    
192
  def GetMasterCandidates(self):
193
    """Return the list of master candidates.
194

195
    """
196
    data = self._ReadFile(constants.SS_MASTER_CANDIDATES)
197
    nl = data.splitlines(False)
198
    return nl
199

    
200
  def GetMasterCandidatesIPList(self):
201
    """Return the list of master candidates' primary IP.
202

203
    """
204
    data = self._ReadFile(constants.SS_MASTER_CANDIDATES_IPS)
205
    nl = data.splitlines(False)
206
    return nl
207

    
208
  def GetMasterIP(self):
209
    """Get the IP of the master node for this cluster.
210

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

    
214
  def GetMasterNetdev(self):
215
    """Get the netdev to which we'll add the master ip.
216

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

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

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

    
226
  def GetNodeList(self):
227
    """Return the list of cluster nodes.
228

229
    """
230
    data = self._ReadFile(constants.SS_NODE_LIST)
231
    nl = data.splitlines(False)
232
    return nl
233

    
234
  def GetNodePrimaryIPList(self):
235
    """Return the list of cluster nodes' primary IP.
236

237
    """
238
    data = self._ReadFile(constants.SS_NODE_PRIMARY_IPS)
239
    nl = data.splitlines(False)
240
    return nl
241

    
242
  def GetNodeSecondaryIPList(self):
243
    """Return the list of cluster nodes' secondary IP.
244

245
    """
246
    data = self._ReadFile(constants.SS_NODE_SECONDARY_IPS)
247
    nl = data.splitlines(False)
248
    return nl
249

    
250
  def GetClusterTags(self):
251
    """Return the cluster tags.
252

253
    """
254
    data = self._ReadFile(constants.SS_CLUSTER_TAGS)
255
    nl = data.splitlines(False)
256
    return nl
257

    
258

    
259
def GetMasterAndMyself(ss=None):
260
  """Get the master node and my own hostname.
261

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

265
  The function does not handle any errors, these should be handled in
266
  the caller (errors.ConfigurationError, errors.ResolverError).
267

268
  @param ss: either a sstore.SimpleConfigReader or a
269
      sstore.SimpleStore instance
270
  @rtype: tuple
271
  @return: a tuple (master node name, my own name)
272

273
  """
274
  if ss is None:
275
    ss = SimpleStore()
276
  return ss.GetMasterNode(), utils.HostInfo().name
277

    
278

    
279
def CheckMaster(debug, ss=None):
280
  """Checks the node setup.
281

282
  If this is the master, the function will return. Otherwise it will
283
  exit with an exit code based on the node status.
284

285
  """
286
  try:
287
    master_name, myself = GetMasterAndMyself(ss)
288
  except errors.ConfigurationError, err:
289
    print "Cluster configuration incomplete: '%s'" % str(err)
290
    sys.exit(constants.EXIT_NODESETUP_ERROR)
291
  except errors.ResolverError, err:
292
    sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0])
293
    sys.exit(constants.EXIT_NODESETUP_ERROR)
294

    
295
  if myself != master_name:
296
    if debug:
297
      sys.stderr.write("Not master, exiting.\n")
298
    sys.exit(constants.EXIT_NOTMASTER)
299

    
300

    
301
def CheckMasterCandidate(debug, ss=None):
302
  """Checks the node setup.
303

304
  If this is a master candidate, the function will return. Otherwise it will
305
  exit with an exit code based on the node status.
306

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

    
320
  if myself not in candidates:
321
    if debug:
322
      sys.stderr.write("Not master candidate, exiting.\n")
323
    sys.exit(constants.EXIT_NOTCANDIDATE)
324