Statistics
| Branch: | Tag: | Revision:

root / lib / ssconf.py @ a33848a5

History | View | Annotate | Download (7.1 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_OFFLINE_NODES,
105
    constants.SS_ONLINE_NODES,
106
    constants.SS_INSTANCE_LIST,
107
    constants.SS_RELEASE_VERSION,
108
    )
109
  _MAX_SIZE = 131072
110

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

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

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

    
125
    filename = self._cfg_dir + '/' + self._SS_FILEPREFIX + key
126
    return filename
127

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

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

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

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

147
    @type values: dict
148
    @param values: Dictionary of (name, value)
149

150
    """
151
    ssconf_lock = utils.FileLock(constants.SSCONF_LOCK_FILE)
152

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

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

166
    This is used for computing node replication data.
167

168
    """
169
    return [self.KeyToFilename(key) for key in self._VALID_KEYS]
170

    
171
  def GetClusterName(self):
172
    """Get the cluster name.
173

174
    """
175
    return self._ReadFile(constants.SS_CLUSTER_NAME)
176

    
177
  def GetFileStorageDir(self):
178
    """Get the file storage dir.
179

180
    """
181
    return self._ReadFile(constants.SS_FILE_STORAGE_DIR)
182

    
183
  def GetMasterCandidates(self):
184
    """Return the list of master candidates.
185

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

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

194
    """
195
    return self._ReadFile(constants.SS_MASTER_IP)
196

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

200
    """
201
    return self._ReadFile(constants.SS_MASTER_NETDEV)
202

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

206
    """
207
    return self._ReadFile(constants.SS_MASTER_NODE)
208

    
209
  def GetNodeList(self):
210
    """Return the list of cluster nodes.
211

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

    
217
  def GetClusterTags(self):
218
    """Return the cluster tags.
219

220
    """
221
    data = self._ReadFile(constants.SS_CLUSTER_TAGS)
222
    nl = data.splitlines(False)
223
    return nl
224

    
225

    
226
def GetMasterAndMyself(ss=None):
227
  """Get the master node and my own hostname.
228

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

232
  The function does not handle any errors, these should be handled in
233
  the caller (errors.ConfigurationError, errors.ResolverError).
234

235
  @param ss: either a sstore.SimpleConfigReader or a
236
      sstore.SimpleStore instance
237
  @rtype: tuple
238
  @return: a tuple (master node name, my own name)
239

240
  """
241
  if ss is None:
242
    ss = SimpleStore()
243
  return ss.GetMasterNode(), utils.HostInfo().name
244

    
245

    
246
def CheckMaster(debug, ss=None):
247
  """Checks the node setup.
248

249
  If this is the master, the function will return. Otherwise it will
250
  exit with an exit code based on the node status.
251

252
  """
253
  try:
254
    master_name, myself = GetMasterAndMyself(ss)
255
  except errors.ConfigurationError, err:
256
    print "Cluster configuration incomplete: '%s'" % str(err)
257
    sys.exit(constants.EXIT_NODESETUP_ERROR)
258
  except errors.ResolverError, err:
259
    sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0])
260
    sys.exit(constants.EXIT_NODESETUP_ERROR)
261

    
262
  if myself != master_name:
263
    if debug:
264
      sys.stderr.write("Not master, exiting.\n")
265
    sys.exit(constants.EXIT_NOTMASTER)