Statistics
| Branch: | Tag: | Revision:

root / lib / ssconf.py @ fb486969

History | View | Annotate | Download (9.4 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2010, 2011, 2012 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 errno
31

    
32
from ganeti import errors
33
from ganeti import constants
34
from ganeti import utils
35
from ganeti import netutils
36
from ganeti import pathutils
37

    
38

    
39
SSCONF_LOCK_TIMEOUT = 10
40

    
41
#: Valid ssconf keys
42
_VALID_KEYS = frozenset([
43
  constants.SS_CLUSTER_NAME,
44
  constants.SS_CLUSTER_TAGS,
45
  constants.SS_FILE_STORAGE_DIR,
46
  constants.SS_SHARED_FILE_STORAGE_DIR,
47
  constants.SS_MASTER_CANDIDATES,
48
  constants.SS_MASTER_CANDIDATES_IPS,
49
  constants.SS_MASTER_IP,
50
  constants.SS_MASTER_NETDEV,
51
  constants.SS_MASTER_NETMASK,
52
  constants.SS_MASTER_NODE,
53
  constants.SS_NODE_LIST,
54
  constants.SS_NODE_PRIMARY_IPS,
55
  constants.SS_NODE_SECONDARY_IPS,
56
  constants.SS_OFFLINE_NODES,
57
  constants.SS_ONLINE_NODES,
58
  constants.SS_PRIMARY_IP_FAMILY,
59
  constants.SS_INSTANCE_LIST,
60
  constants.SS_RELEASE_VERSION,
61
  constants.SS_HYPERVISOR_LIST,
62
  constants.SS_MAINTAIN_NODE_HEALTH,
63
  constants.SS_UID_POOL,
64
  constants.SS_NODEGROUPS,
65
  constants.SS_NETWORKS,
66
  ])
67

    
68
#: Maximum size for ssconf files
69
_MAX_SIZE = 128 * 1024
70

    
71

    
72
class SimpleStore(object):
73
  """Interface to static cluster data.
74

75
  This is different that the config.ConfigWriter and
76
  SimpleConfigReader classes in that it holds data that will always be
77
  present, even on nodes which don't have all the cluster data.
78

79
  Other particularities of the datastore:
80
    - keys are restricted to predefined values
81

82
  """
83
  def __init__(self, cfg_location=None):
84
    if cfg_location is None:
85
      self._cfg_dir = pathutils.DATA_DIR
86
    else:
87
      self._cfg_dir = cfg_location
88

    
89
  def KeyToFilename(self, key):
90
    """Convert a given key into filename.
91

92
    """
93
    if key not in _VALID_KEYS:
94
      raise errors.ProgrammerError("Invalid key requested from SSConf: '%s'"
95
                                   % str(key))
96

    
97
    filename = self._cfg_dir + "/" + constants.SSCONF_FILEPREFIX + key
98
    return filename
99

    
100
  def _ReadFile(self, key, default=None):
101
    """Generic routine to read keys.
102

103
    This will read the file which holds the value requested. Errors
104
    will be changed into ConfigurationErrors.
105

106
    """
107
    filename = self.KeyToFilename(key)
108
    try:
109
      data = utils.ReadFile(filename, size=_MAX_SIZE)
110
    except EnvironmentError, err:
111
      if err.errno == errno.ENOENT and default is not None:
112
        return default
113
      raise errors.ConfigurationError("Can't read ssconf file %s: %s" %
114
                                      (filename, str(err)))
115

    
116
    return data.rstrip("\n")
117

    
118
  def WriteFiles(self, values):
119
    """Writes ssconf files used by external scripts.
120

121
    @type values: dict
122
    @param values: Dictionary of (name, value)
123

124
    """
125
    ssconf_lock = utils.FileLock.Open(pathutils.SSCONF_LOCK_FILE)
126

    
127
    # Get lock while writing files
128
    ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT)
129
    try:
130
      for name, value in values.iteritems():
131
        if value and not value.endswith("\n"):
132
          value += "\n"
133
        if len(value) > _MAX_SIZE:
134
          raise errors.ConfigurationError("ssconf file %s above maximum size" %
135
                                          name)
136
        utils.WriteFile(self.KeyToFilename(name), data=value,
137
                        mode=constants.SS_FILE_PERMS)
138
    finally:
139
      ssconf_lock.Unlock()
140

    
141
  def GetFileList(self):
142
    """Return the list of all config files.
143

144
    This is used for computing node replication data.
145

146
    """
147
    return [self.KeyToFilename(key) for key in _VALID_KEYS]
148

    
149
  def GetClusterName(self):
150
    """Get the cluster name.
151

152
    """
153
    return self._ReadFile(constants.SS_CLUSTER_NAME)
154

    
155
  def GetFileStorageDir(self):
156
    """Get the file storage dir.
157

158
    """
159
    return self._ReadFile(constants.SS_FILE_STORAGE_DIR)
160

    
161
  def GetSharedFileStorageDir(self):
162
    """Get the shared file storage dir.
163

164
    """
165
    return self._ReadFile(constants.SS_SHARED_FILE_STORAGE_DIR)
166

    
167
  def GetMasterCandidates(self):
168
    """Return the list of master candidates.
169

170
    """
171
    data = self._ReadFile(constants.SS_MASTER_CANDIDATES)
172
    nl = data.splitlines(False)
173
    return nl
174

    
175
  def GetMasterCandidatesIPList(self):
176
    """Return the list of master candidates' primary IP.
177

178
    """
179
    data = self._ReadFile(constants.SS_MASTER_CANDIDATES_IPS)
180
    nl = data.splitlines(False)
181
    return nl
182

    
183
  def GetMasterIP(self):
184
    """Get the IP of the master node for this cluster.
185

186
    """
187
    return self._ReadFile(constants.SS_MASTER_IP)
188

    
189
  def GetMasterNetdev(self):
190
    """Get the netdev to which we'll add the master ip.
191

192
    """
193
    return self._ReadFile(constants.SS_MASTER_NETDEV)
194

    
195
  def GetMasterNetmask(self):
196
    """Get the master netmask.
197

198
    """
199
    try:
200
      return self._ReadFile(constants.SS_MASTER_NETMASK)
201
    except errors.ConfigurationError:
202
      family = self.GetPrimaryIPFamily()
203
      ipcls = netutils.IPAddress.GetClassFromIpFamily(family)
204
      return ipcls.iplen
205

    
206
  def GetMasterNode(self):
207
    """Get the hostname of the master node for this cluster.
208

209
    """
210
    return self._ReadFile(constants.SS_MASTER_NODE)
211

    
212
  def GetNodeList(self):
213
    """Return the list of cluster nodes.
214

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

    
220
  def GetNodePrimaryIPList(self):
221
    """Return the list of cluster nodes' primary IP.
222

223
    """
224
    data = self._ReadFile(constants.SS_NODE_PRIMARY_IPS)
225
    nl = data.splitlines(False)
226
    return nl
227

    
228
  def GetNodeSecondaryIPList(self):
229
    """Return the list of cluster nodes' secondary IP.
230

231
    """
232
    data = self._ReadFile(constants.SS_NODE_SECONDARY_IPS)
233
    nl = data.splitlines(False)
234
    return nl
235

    
236
  def GetNodegroupList(self):
237
    """Return the list of nodegroups.
238

239
    """
240
    data = self._ReadFile(constants.SS_NODEGROUPS)
241
    nl = data.splitlines(False)
242
    return nl
243

    
244
  def GetNetworkList(self):
245
    """Return the list of networks.
246

247
    """
248
    data = self._ReadFile(constants.SS_NETWORKS)
249
    nl = data.splitlines(False)
250
    return nl
251

    
252
  def GetClusterTags(self):
253
    """Return the cluster tags.
254

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

    
260
  def GetHypervisorList(self):
261
    """Return the list of enabled hypervisors.
262

263
    """
264
    data = self._ReadFile(constants.SS_HYPERVISOR_LIST)
265
    nl = data.splitlines(False)
266
    return nl
267

    
268
  def GetMaintainNodeHealth(self):
269
    """Return the value of the maintain_node_health option.
270

271
    """
272
    data = self._ReadFile(constants.SS_MAINTAIN_NODE_HEALTH)
273
    # we rely on the bool serialization here
274
    return data == "True"
275

    
276
  def GetUidPool(self):
277
    """Return the user-id pool definition string.
278

279
    The separator character is a newline.
280

281
    The return value can be parsed using uidpool.ParseUidPool()::
282

283
      ss = ssconf.SimpleStore()
284
      uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\\n")
285

286
    """
287
    data = self._ReadFile(constants.SS_UID_POOL)
288
    return data
289

    
290
  def GetPrimaryIPFamily(self):
291
    """Return the cluster-wide primary address family.
292

293
    """
294
    try:
295
      return int(self._ReadFile(constants.SS_PRIMARY_IP_FAMILY,
296
                                default=netutils.IP4Address.family))
297
    except (ValueError, TypeError), err:
298
      raise errors.ConfigurationError("Error while trying to parse primary IP"
299
                                      " family: %s" % err)
300

    
301

    
302
def WriteSsconfFiles(values):
303
  """Update all ssconf files.
304

305
  Wrapper around L{SimpleStore.WriteFiles}.
306

307
  """
308
  SimpleStore().WriteFiles(values)
309

    
310

    
311
def GetMasterAndMyself(ss=None):
312
  """Get the master node and my own hostname.
313

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

317
  The function does not handle any errors, these should be handled in
318
  the caller (errors.ConfigurationError, errors.ResolverError).
319

320
  @param ss: either a sstore.SimpleConfigReader or a
321
      sstore.SimpleStore instance
322
  @rtype: tuple
323
  @return: a tuple (master node name, my own name)
324

325
  """
326
  if ss is None:
327
    ss = SimpleStore()
328
  return ss.GetMasterNode(), netutils.Hostname.GetSysName()
329

    
330

    
331
def CheckMaster(debug, ss=None):
332
  """Checks the node setup.
333

334
  If this is the master, the function will return. Otherwise it will
335
  exit with an exit code based on the node status.
336

337
  """
338
  try:
339
    master_name, myself = GetMasterAndMyself(ss)
340
  except errors.ConfigurationError, err:
341
    print "Cluster configuration incomplete: '%s'" % str(err)
342
    sys.exit(constants.EXIT_NODESETUP_ERROR)
343
  except errors.ResolverError, err:
344
    sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0])
345
    sys.exit(constants.EXIT_NODESETUP_ERROR)
346

    
347
  if myself != master_name:
348
    if debug:
349
      sys.stderr.write("Not master, exiting.\n")
350
    sys.exit(constants.EXIT_NOTMASTER)