Statistics
| Branch: | Tag: | Revision:

root / lib / ssconf.py @ 59b4eeef

History | View | Annotate | Download (7.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._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
  @classmethod
83
  def FromDict(cls, val, cfg_file=constants.CLUSTER_CONF_FILE):
84
    """Alternative construction from a dictionary.
85

86
    """
87
    obj = SimpleConfigReader.__new__(cls)
88
    obj._config_data = val
89
    obj._file_name = cfg_file
90
    return obj
91

    
92

    
93
class SimpleConfigWriter(SimpleConfigReader):
94
  """Simple class to write configuration file.
95

96
  """
97
  def SetMasterNode(self, node):
98
    """Change master node.
99

100
    """
101
    self._config_data["cluster"]["master_node"] = node
102

    
103
  def Save(self):
104
    """Writes configuration file.
105

106
    Warning: Doesn't take care of locking or synchronizing with other
107
    processes.
108

109
    """
110
    utils.WriteFile(self._file_name,
111
                    data=serializer.Dump(self._config_data),
112
                    mode=0600)
113

    
114

    
115
class SimpleStore(object):
116
  """Interface to static cluster data.
117

118
  This is different that the config.ConfigWriter and
119
  SimpleConfigReader classes in that it holds data that will always be
120
  present, even on nodes which don't have all the cluster data.
121

122
  Other particularities of the datastore:
123
    - keys are restricted to predefined values
124

125
  """
126
  _SS_FILEPREFIX = "ssconf_"
127
  _VALID_KEYS = (
128
    constants.SS_CLUSTER_NAME,
129
    constants.SS_CLUSTER_TAGS,
130
    constants.SS_FILE_STORAGE_DIR,
131
    constants.SS_MASTER_CANDIDATES,
132
    constants.SS_MASTER_IP,
133
    constants.SS_MASTER_NETDEV,
134
    constants.SS_MASTER_NODE,
135
    constants.SS_NODE_LIST,
136
    constants.SS_OFFLINE_NODES,
137
    constants.SS_ONLINE_NODES,
138
    constants.SS_INSTANCE_LIST,
139
    constants.SS_RELEASE_VERSION,
140
    )
141
  _MAX_SIZE = 131072
142

    
143
  def __init__(self, cfg_location=None):
144
    if cfg_location is None:
145
      self._cfg_dir = constants.DATA_DIR
146
    else:
147
      self._cfg_dir = cfg_location
148

    
149
  def KeyToFilename(self, key):
150
    """Convert a given key into filename.
151

152
    """
153
    if key not in self._VALID_KEYS:
154
      raise errors.ProgrammerError("Invalid key requested from SSConf: '%s'"
155
                                   % str(key))
156

    
157
    filename = self._cfg_dir + '/' + self._SS_FILEPREFIX + key
158
    return filename
159

    
160
  def _ReadFile(self, key):
161
    """Generic routine to read keys.
162

163
    This will read the file which holds the value requested. Errors
164
    will be changed into ConfigurationErrors.
165

166
    """
167
    filename = self.KeyToFilename(key)
168
    try:
169
      fh = file(filename, 'r')
170
      try:
171
        data = fh.read(self._MAX_SIZE)
172
        data = data.rstrip('\n')
173
      finally:
174
        fh.close()
175
    except EnvironmentError, err:
176
      raise errors.ConfigurationError("Can't read from the ssconf file:"
177
                                      " '%s'" % str(err))
178
    return data
179

    
180
  def WriteFiles(self, values):
181
    """Writes ssconf files used by external scripts.
182

183
    @type values: dict
184
    @param values: Dictionary of (name, value)
185

186
    """
187
    ssconf_lock = utils.FileLock(constants.SSCONF_LOCK_FILE)
188

    
189
    # Get lock while writing files
190
    ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT)
191
    try:
192
      for name, value in values.iteritems():
193
        if value and not value.endswith("\n"):
194
          value += "\n"
195
        utils.WriteFile(self.KeyToFilename(name), data=value, mode=0444)
196
    finally:
197
      ssconf_lock.Unlock()
198

    
199
  def GetFileList(self):
200
    """Return the list of all config files.
201

202
    This is used for computing node replication data.
203

204
    """
205
    return [self.KeyToFilename(key) for key in self._VALID_KEYS]
206

    
207
  def GetClusterName(self):
208
    """Get the cluster name.
209

210
    """
211
    return self._ReadFile(constants.SS_CLUSTER_NAME)
212

    
213
  def GetFileStorageDir(self):
214
    """Get the file storage dir.
215

216
    """
217
    return self._ReadFile(constants.SS_FILE_STORAGE_DIR)
218

    
219
  def GetMasterCandidates(self):
220
    """Return the list of master candidates.
221

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

    
227
  def GetMasterIP(self):
228
    """Get the IP of the master node for this cluster.
229

230
    """
231
    return self._ReadFile(constants.SS_MASTER_IP)
232

    
233
  def GetMasterNetdev(self):
234
    """Get the netdev to which we'll add the master ip.
235

236
    """
237
    return self._ReadFile(constants.SS_MASTER_NETDEV)
238

    
239
  def GetMasterNode(self):
240
    """Get the hostname of the master node for this cluster.
241

242
    """
243
    return self._ReadFile(constants.SS_MASTER_NODE)
244

    
245
  def GetNodeList(self):
246
    """Return the list of cluster nodes.
247

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

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

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

    
261

    
262
def GetMasterAndMyself(ss=None):
263
  """Get the master node and my own hostname.
264

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

268
  The function does not handle any errors, these should be handled in
269
  the caller (errors.ConfigurationError, errors.ResolverError).
270

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

276
  """
277
  if ss is None:
278
    ss = SimpleStore()
279
  return ss.GetMasterNode(), utils.HostInfo().name
280

    
281

    
282
def CheckMaster(debug, ss=None):
283
  """Checks the node setup.
284

285
  If this is the master, the function will return. Otherwise it will
286
  exit with an exit code based on the node status.
287

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

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