cmdlib: Change tasklet logging to debug level
[ganeti-local] / lib / ssconf.py
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)