Statistics
| Branch: | Tag: | Revision:

root / lib / ssconf.py @ f95c81bf

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
  @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 Save(self):
98
    """Writes configuration file.
99

100
    Warning: Doesn't take care of locking or synchronizing with other
101
    processes.
102

103
    """
104
    utils.WriteFile(self._file_name,
105
                    data=serializer.Dump(self._config_data),
106
                    mode=0600)
107

    
108

    
109
class SimpleStore(object):
110
  """Interface to static cluster data.
111

112
  This is different that the config.ConfigWriter and
113
  SimpleConfigReader classes in that it holds data that will always be
114
  present, even on nodes which don't have all the cluster data.
115

116
  Other particularities of the datastore:
117
    - keys are restricted to predefined values
118

119
  """
120
  _SS_FILEPREFIX = "ssconf_"
121
  _VALID_KEYS = (
122
    constants.SS_CLUSTER_NAME,
123
    constants.SS_CLUSTER_TAGS,
124
    constants.SS_FILE_STORAGE_DIR,
125
    constants.SS_MASTER_CANDIDATES,
126
    constants.SS_MASTER_IP,
127
    constants.SS_MASTER_NETDEV,
128
    constants.SS_MASTER_NODE,
129
    constants.SS_NODE_LIST,
130
    constants.SS_OFFLINE_NODES,
131
    constants.SS_ONLINE_NODES,
132
    constants.SS_INSTANCE_LIST,
133
    constants.SS_RELEASE_VERSION,
134
    )
135
  _MAX_SIZE = 131072
136

    
137
  def __init__(self, cfg_location=None):
138
    if cfg_location is None:
139
      self._cfg_dir = constants.DATA_DIR
140
    else:
141
      self._cfg_dir = cfg_location
142

    
143
  def KeyToFilename(self, key):
144
    """Convert a given key into filename.
145

146
    """
147
    if key not in self._VALID_KEYS:
148
      raise errors.ProgrammerError("Invalid key requested from SSConf: '%s'"
149
                                   % str(key))
150

    
151
    filename = self._cfg_dir + '/' + self._SS_FILEPREFIX + key
152
    return filename
153

    
154
  def _ReadFile(self, key):
155
    """Generic routine to read keys.
156

157
    This will read the file which holds the value requested. Errors
158
    will be changed into ConfigurationErrors.
159

160
    """
161
    filename = self.KeyToFilename(key)
162
    try:
163
      fh = file(filename, 'r')
164
      try:
165
        data = fh.read(self._MAX_SIZE)
166
        data = data.rstrip('\n')
167
      finally:
168
        fh.close()
169
    except EnvironmentError, err:
170
      raise errors.ConfigurationError("Can't read from the ssconf file:"
171
                                      " '%s'" % str(err))
172
    return data
173

    
174
  def WriteFiles(self, values):
175
    """Writes ssconf files used by external scripts.
176

177
    @type values: dict
178
    @param values: Dictionary of (name, value)
179

180
    """
181
    ssconf_lock = utils.FileLock(constants.SSCONF_LOCK_FILE)
182

    
183
    # Get lock while writing files
184
    ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT)
185
    try:
186
      for name, value in values.iteritems():
187
        if value and not value.endswith("\n"):
188
          value += "\n"
189
        utils.WriteFile(self.KeyToFilename(name), data=value, mode=0444)
190
    finally:
191
      ssconf_lock.Unlock()
192

    
193
  def GetFileList(self):
194
    """Return the list of all config files.
195

196
    This is used for computing node replication data.
197

198
    """
199
    return [self.KeyToFilename(key) for key in self._VALID_KEYS]
200

    
201
  def GetClusterName(self):
202
    """Get the cluster name.
203

204
    """
205
    return self._ReadFile(constants.SS_CLUSTER_NAME)
206

    
207
  def GetFileStorageDir(self):
208
    """Get the file storage dir.
209

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

    
213
  def GetMasterCandidates(self):
214
    """Return the list of master candidates.
215

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

    
221
  def GetMasterIP(self):
222
    """Get the IP of the master node for this cluster.
223

224
    """
225
    return self._ReadFile(constants.SS_MASTER_IP)
226

    
227
  def GetMasterNetdev(self):
228
    """Get the netdev to which we'll add the master ip.
229

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

    
233
  def GetMasterNode(self):
234
    """Get the hostname of the master node for this cluster.
235

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

    
239
  def GetNodeList(self):
240
    """Return the list of cluster nodes.
241

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

    
247
  def GetClusterTags(self):
248
    """Return the cluster tags.
249

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

    
255

    
256
def GetMasterAndMyself(ss=None):
257
  """Get the master node and my own hostname.
258

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

262
  The function does not handle any errors, these should be handled in
263
  the caller (errors.ConfigurationError, errors.ResolverError).
264

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

270
  """
271
  if ss is None:
272
    ss = SimpleStore()
273
  return ss.GetMasterNode(), utils.HostInfo().name
274

    
275

    
276
def CheckMaster(debug, ss=None):
277
  """Checks the node setup.
278

279
  If this is the master, the function will return. Otherwise it will
280
  exit with an exit code based on the node status.
281

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

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