Statistics
| Branch: | Tag: | Revision:

root / lib / ssconf.py @ 8113a52e

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

    
83
class SimpleStore(object):
84
  """Interface to static cluster data.
85

86
  This is different that the config.ConfigWriter and
87
  SimpleConfigReader classes in that it holds data that will always be
88
  present, even on nodes which don't have all the cluster data.
89

90
  Other particularities of the datastore:
91
    - keys are restricted to predefined values
92

93
  """
94
  _SS_FILEPREFIX = "ssconf_"
95
  _VALID_KEYS = (
96
    constants.SS_CLUSTER_NAME,
97
    constants.SS_CLUSTER_TAGS,
98
    constants.SS_FILE_STORAGE_DIR,
99
    constants.SS_MASTER_CANDIDATES,
100
    constants.SS_MASTER_CANDIDATES_IPS,
101
    constants.SS_MASTER_IP,
102
    constants.SS_MASTER_NETDEV,
103
    constants.SS_MASTER_NODE,
104
    constants.SS_NODE_LIST,
105
    constants.SS_NODE_PRIMARY_IPS,
106
    constants.SS_NODE_SECONDARY_IPS,
107
    constants.SS_OFFLINE_NODES,
108
    constants.SS_ONLINE_NODES,
109
    constants.SS_INSTANCE_LIST,
110
    constants.SS_RELEASE_VERSION,
111
    )
112
  _MAX_SIZE = 131072
113

    
114
  def __init__(self, cfg_location=None):
115
    if cfg_location is None:
116
      self._cfg_dir = constants.DATA_DIR
117
    else:
118
      self._cfg_dir = cfg_location
119

    
120
  def KeyToFilename(self, key):
121
    """Convert a given key into filename.
122

123
    """
124
    if key not in self._VALID_KEYS:
125
      raise errors.ProgrammerError("Invalid key requested from SSConf: '%s'"
126
                                   % str(key))
127

    
128
    filename = self._cfg_dir + '/' + self._SS_FILEPREFIX + key
129
    return filename
130

    
131
  def _ReadFile(self, key):
132
    """Generic routine to read keys.
133

134
    This will read the file which holds the value requested. Errors
135
    will be changed into ConfigurationErrors.
136

137
    """
138
    filename = self.KeyToFilename(key)
139
    try:
140
      data = utils.ReadFile(filename, size=self._MAX_SIZE)
141
    except EnvironmentError, err:
142
      raise errors.ConfigurationError("Can't read from the ssconf file:"
143
                                      " '%s'" % str(err))
144
    data = data.rstrip('\n')
145
    return data
146

    
147
  def WriteFiles(self, values):
148
    """Writes ssconf files used by external scripts.
149

150
    @type values: dict
151
    @param values: Dictionary of (name, value)
152

153
    """
154
    ssconf_lock = utils.FileLock(constants.SSCONF_LOCK_FILE)
155

    
156
    # Get lock while writing files
157
    ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT)
158
    try:
159
      for name, value in values.iteritems():
160
        if value and not value.endswith("\n"):
161
          value += "\n"
162
        utils.WriteFile(self.KeyToFilename(name), data=value, mode=0444)
163
    finally:
164
      ssconf_lock.Unlock()
165

    
166
  def GetFileList(self):
167
    """Return the list of all config files.
168

169
    This is used for computing node replication data.
170

171
    """
172
    return [self.KeyToFilename(key) for key in self._VALID_KEYS]
173

    
174
  def GetClusterName(self):
175
    """Get the cluster name.
176

177
    """
178
    return self._ReadFile(constants.SS_CLUSTER_NAME)
179

    
180
  def GetFileStorageDir(self):
181
    """Get the file storage dir.
182

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

    
186
  def GetMasterCandidates(self):
187
    """Return the list of master candidates.
188

189
    """
190
    data = self._ReadFile(constants.SS_MASTER_CANDIDATES)
191
    nl = data.splitlines(False)
192
    return nl
193

    
194
  def GetMasterCandidatesIPList(self):
195
    """Return the list of master candidates' primary IP.
196

197
    """
198
    data = self._ReadFile(constants.SS_MASTER_CANDIDATES_IPS)
199
    nl = data.splitlines(False)
200
    return nl
201

    
202
  def GetMasterIP(self):
203
    """Get the IP of the master node for this cluster.
204

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

    
208
  def GetMasterNetdev(self):
209
    """Get the netdev to which we'll add the master ip.
210

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

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

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

    
220
  def GetNodeList(self):
221
    """Return the list of cluster nodes.
222

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

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

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

    
236
  def GetNodeSecondaryIPList(self):
237
    """Return the list of cluster nodes' secondary IP.
238

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

    
244
  def GetClusterTags(self):
245
    """Return the cluster tags.
246

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

    
252

    
253
def GetMasterAndMyself(ss=None):
254
  """Get the master node and my own hostname.
255

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

259
  The function does not handle any errors, these should be handled in
260
  the caller (errors.ConfigurationError, errors.ResolverError).
261

262
  @param ss: either a sstore.SimpleConfigReader or a
263
      sstore.SimpleStore instance
264
  @rtype: tuple
265
  @return: a tuple (master node name, my own name)
266

267
  """
268
  if ss is None:
269
    ss = SimpleStore()
270
  return ss.GetMasterNode(), utils.HostInfo().name
271

    
272

    
273
def CheckMaster(debug, ss=None):
274
  """Checks the node setup.
275

276
  If this is the master, the function will return. Otherwise it will
277
  exit with an exit code based on the node status.
278

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

    
289
  if myself != master_name:
290
    if debug:
291
      sys.stderr.write("Not master, exiting.\n")
292
    sys.exit(constants.EXIT_NOTMASTER)