Statistics
| Branch: | Tag: | Revision:

root / lib / ssconf.py @ f9780ccd

History | View | Annotate | Download (7.6 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_IP,
101
    constants.SS_MASTER_NETDEV,
102
    constants.SS_MASTER_NODE,
103
    constants.SS_NODE_LIST,
104
    constants.SS_NODE_PRIMARY_IPS,
105
    constants.SS_NODE_SECONDARY_IPS,
106
    constants.SS_OFFLINE_NODES,
107
    constants.SS_ONLINE_NODES,
108
    constants.SS_INSTANCE_LIST,
109
    constants.SS_RELEASE_VERSION,
110
    )
111
  _MAX_SIZE = 131072
112

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

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

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

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

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

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

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

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

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

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

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

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

168
    This is used for computing node replication data.
169

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

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

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

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

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

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

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

    
193
  def GetMasterIP(self):
194
    """Get the IP of the master node for this cluster.
195

196
    """
197
    return self._ReadFile(constants.SS_MASTER_IP)
198

    
199
  def GetMasterNetdev(self):
200
    """Get the netdev to which we'll add the master ip.
201

202
    """
203
    return self._ReadFile(constants.SS_MASTER_NETDEV)
204

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

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

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

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

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

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

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

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

    
235
  def GetClusterTags(self):
236
    """Return the cluster tags.
237

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

    
243

    
244
def GetMasterAndMyself(ss=None):
245
  """Get the master node and my own hostname.
246

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

250
  The function does not handle any errors, these should be handled in
251
  the caller (errors.ConfigurationError, errors.ResolverError).
252

253
  @param ss: either a sstore.SimpleConfigReader or a
254
      sstore.SimpleStore instance
255
  @rtype: tuple
256
  @return: a tuple (master node name, my own name)
257

258
  """
259
  if ss is None:
260
    ss = SimpleStore()
261
  return ss.GetMasterNode(), utils.HostInfo().name
262

    
263

    
264
def CheckMaster(debug, ss=None):
265
  """Checks the node setup.
266

267
  If this is the master, the function will return. Otherwise it will
268
  exit with an exit code based on the node status.
269

270
  """
271
  try:
272
    master_name, myself = GetMasterAndMyself(ss)
273
  except errors.ConfigurationError, err:
274
    print "Cluster configuration incomplete: '%s'" % str(err)
275
    sys.exit(constants.EXIT_NODESETUP_ERROR)
276
  except errors.ResolverError, err:
277
    sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0])
278
    sys.exit(constants.EXIT_NODESETUP_ERROR)
279

    
280
  if myself != master_name:
281
    if debug:
282
      sys.stderr.write("Not master, exiting.\n")
283
    sys.exit(constants.EXIT_NOTMASTER)