Pass ssconf values from master to node
[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   @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 def _SsconfPath(name):
116   if not RE_VALID_SSCONF_NAME.match(name):
117     raise errors.ParameterError("Invalid ssconf name: %s" % name)
118   return "%s/ssconf_%s" % (constants.DATA_DIR, name)
119
120
121 def WriteSsconfFiles(values):
122   """Writes legacy ssconf files to be used by external scripts.
123
124   @type values: dict
125   @param values: Dictionary of (name, value)
126
127   """
128   ssconf_lock = utils.FileLock(constants.SSCONF_LOCK_FILE)
129
130   # Get lock while writing files
131   ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT)
132   try:
133     for name, value in values.iteritems():
134       if not value.endswith("\n"):
135         value += "\n"
136       utils.WriteFile(_SsconfPath(name),
137                       data=value)
138   finally:
139     ssconf_lock.Unlock()
140
141
142 def GetMasterAndMyself(ss=None):
143   """Get the master node and my own hostname.
144
145   This can be either used for a 'soft' check (compared to CheckMaster,
146   which exits) or just for computing both at the same time.
147
148   The function does not handle any errors, these should be handled in
149   the caller (errors.ConfigurationError, errors.ResolverError).
150
151   """
152   if ss is None:
153     ss = SimpleConfigReader()
154   return ss.GetMasterNode(), utils.HostInfo().name
155
156
157 def CheckMaster(debug, ss=None):
158   """Checks the node setup.
159
160   If this is the master, the function will return. Otherwise it will
161   exit with an exit code based on the node status.
162
163   """
164   try:
165     master_name, myself = GetMasterAndMyself(ss)
166   except errors.ConfigurationError, err:
167     print "Cluster configuration incomplete: '%s'" % str(err)
168     sys.exit(constants.EXIT_NODESETUP_ERROR)
169   except errors.ResolverError, err:
170     sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0])
171     sys.exit(constants.EXIT_NODESETUP_ERROR)
172
173   if myself != master_name:
174     if debug:
175       sys.stderr.write("Not master, exiting.\n")
176     sys.exit(constants.EXIT_NOTMASTER)