Merge branch 'stable-2.16' into stable-2.17
[ganeti-github.git] / lib / runtime.py
1 #
2 #
3
4 # Copyright (C) 2010 Google Inc.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
9 # met:
10 #
11 # 1. Redistributions of source code must retain the above copyright notice,
12 # this list of conditions and the following disclaimer.
13 #
14 # 2. Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in the
16 # documentation and/or other materials provided with the distribution.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19 # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 """Module implementing configuration details at runtime.
31
32 """
33
34
35 import grp
36 import pwd
37 import threading
38 import platform
39
40 from ganeti import constants
41 from ganeti import errors
42 from ganeti import luxi
43 from ganeti.rpc.errors import NoMasterError
44 from ganeti import pathutils
45 from ganeti import ssconf
46 from ganeti import utils
47
48
49 _priv = None
50 _priv_lock = threading.Lock()
51
52 #: Architecture information
53 _arch = None
54
55
56 def GetUid(user, _getpwnam):
57 """Retrieve the uid from the database.
58
59 @type user: string
60 @param user: The username to retrieve
61 @return: The resolved uid
62
63 """
64 try:
65 return _getpwnam(user).pw_uid
66 except KeyError, err:
67 raise errors.ConfigurationError("User '%s' not found (%s)" % (user, err))
68
69
70 def GetGid(group, _getgrnam):
71 """Retrieve the gid from the database.
72
73 @type group: string
74 @param group: The group name to retrieve
75 @return: The resolved gid
76
77 """
78 try:
79 return _getgrnam(group).gr_gid
80 except KeyError, err:
81 raise errors.ConfigurationError("Group '%s' not found (%s)" % (group, err))
82
83
84 class GetentResolver(object):
85 """Resolves Ganeti uids and gids by name.
86
87 @ivar masterd_uid: The resolved uid of the masterd user
88 @ivar masterd_gid: The resolved gid of the masterd group
89 @ivar confd_uid: The resolved uid of the confd user
90 @ivar confd_gid: The resolved gid of the confd group
91 @ivar wconfd_uid: The resolved uid of the wconfd user
92 @ivar wconfd_gid: The resolved gid of the wconfd group
93 @ivar luxid_uid: The resolved uid of the luxid user
94 @ivar luxid_gid: The resolved gid of the luxid group
95 @ivar rapi_uid: The resolved uid of the rapi user
96 @ivar rapi_gid: The resolved gid of the rapi group
97 @ivar noded_uid: The resolved uid of the noded user
98 @ivar daemons_gid: The resolved gid of the daemons group
99 @ivar admin_gid: The resolved gid of the admin group
100
101 """
102 def __init__(self, _getpwnam=pwd.getpwnam, _getgrnam=grp.getgrnam):
103 """Initialize the resolver.
104
105 """
106 # Daemon pairs
107 self.masterd_uid = GetUid(constants.MASTERD_USER, _getpwnam)
108 self.masterd_gid = GetGid(constants.MASTERD_GROUP, _getgrnam)
109
110 self.confd_uid = GetUid(constants.CONFD_USER, _getpwnam)
111 self.confd_gid = GetGid(constants.CONFD_GROUP, _getgrnam)
112
113 self.wconfd_uid = GetUid(constants.WCONFD_USER, _getpwnam)
114 self.wconfd_gid = GetGid(constants.WCONFD_GROUP, _getgrnam)
115
116 self.luxid_uid = GetUid(constants.LUXID_USER, _getpwnam)
117 self.luxid_gid = GetGid(constants.LUXID_GROUP, _getgrnam)
118
119 self.rapi_uid = GetUid(constants.RAPI_USER, _getpwnam)
120 self.rapi_gid = GetGid(constants.RAPI_GROUP, _getgrnam)
121
122 self.noded_uid = GetUid(constants.NODED_USER, _getpwnam)
123 self.noded_gid = GetGid(constants.NODED_GROUP, _getgrnam)
124
125 self.mond_uid = GetUid(constants.MOND_USER, _getpwnam)
126 self.mond_gid = GetGid(constants.MOND_GROUP, _getgrnam)
127
128 # Misc Ganeti groups
129 self.daemons_gid = GetGid(constants.DAEMONS_GROUP, _getgrnam)
130 self.admin_gid = GetGid(constants.ADMIN_GROUP, _getgrnam)
131
132 self._uid2user = {
133 self.masterd_uid: constants.MASTERD_USER,
134 self.confd_uid: constants.CONFD_USER,
135 self.wconfd_uid: constants.WCONFD_USER,
136 self.luxid_uid: constants.LUXID_USER,
137 self.rapi_uid: constants.RAPI_USER,
138 self.noded_uid: constants.NODED_USER,
139 self.mond_uid: constants.MOND_USER,
140 }
141
142 self._gid2group = {
143 self.masterd_gid: constants.MASTERD_GROUP,
144 self.confd_gid: constants.CONFD_GROUP,
145 self.wconfd_gid: constants.WCONFD_GROUP,
146 self.luxid_gid: constants.LUXID_GROUP,
147 self.rapi_gid: constants.RAPI_GROUP,
148 self.noded_gid: constants.NODED_GROUP,
149 self.mond_gid: constants.MOND_GROUP,
150 self.daemons_gid: constants.DAEMONS_GROUP,
151 self.admin_gid: constants.ADMIN_GROUP,
152 }
153
154 self._user2uid = utils.InvertDict(self._uid2user)
155 self._group2gid = utils.InvertDict(self._gid2group)
156
157 def LookupUid(self, uid):
158 """Looks which Ganeti user belongs to this uid.
159
160 @param uid: The uid to lookup
161 @returns The user name associated with that uid
162
163 """
164 try:
165 return self._uid2user[uid]
166 except KeyError:
167 raise errors.ConfigurationError("Unknown Ganeti uid '%d'" % uid)
168
169 def LookupGid(self, gid):
170 """Looks which Ganeti group belongs to this gid.
171
172 @param gid: The gid to lookup
173 @returns The group name associated with that gid
174
175 """
176 try:
177 return self._gid2group[gid]
178 except KeyError:
179 raise errors.ConfigurationError("Unknown Ganeti gid '%d'" % gid)
180
181 def LookupUser(self, name):
182 """Looks which uid belongs to this name.
183
184 @param name: The name to lookup
185 @returns The uid associated with that user name
186
187 """
188 try:
189 return self._user2uid[name]
190 except KeyError:
191 raise errors.ConfigurationError("Unknown Ganeti user '%s'" % name)
192
193 def LookupGroup(self, name):
194 """Looks which gid belongs to this name.
195
196 @param name: The name to lookup
197 @returns The gid associated with that group name
198
199 """
200 try:
201 return self._group2gid[name]
202 except KeyError:
203 raise errors.ConfigurationError("Unknown Ganeti group '%s'" % name)
204
205
206 def GetEnts(resolver=GetentResolver):
207 """Singleton wrapper around resolver instance.
208
209 As this method is accessed by multiple threads at the same time
210 we need to take thread-safety carefully.
211
212 """
213 # We need to use the global keyword here
214 global _priv # pylint: disable=W0603
215
216 if not _priv:
217 _priv_lock.acquire()
218 try:
219 if not _priv:
220 # W0621: Redefine '_priv' from outer scope (used for singleton)
221 _priv = resolver() # pylint: disable=W0621
222 finally:
223 _priv_lock.release()
224
225 return _priv
226
227
228 def InitArchInfo():
229 """Initialize architecture information.
230
231 We can assume this information never changes during the lifetime of a
232 process, therefore the information can easily be cached.
233
234 @note: This function uses C{platform.architecture} to retrieve the Python
235 binary architecture and does so by forking to run C{file} (see Python
236 documentation for more information). Therefore it must not be used in a
237 multi-threaded environment.
238
239 """
240 global _arch # pylint: disable=W0603
241
242 if _arch is not None:
243 raise errors.ProgrammerError("Architecture information can only be"
244 " initialized once")
245
246 _arch = (platform.architecture()[0], platform.machine())
247
248
249 def GetArchInfo():
250 """Returns previsouly initialized architecture information.
251
252 """
253 if _arch is None:
254 raise errors.ProgrammerError("Architecture information hasn't been"
255 " initialized")
256
257 return _arch
258
259
260 def GetClient():
261 """Connects to the a luxi socket and returns a client.
262
263 """
264 try:
265 client = luxi.Client(address=pathutils.QUERY_SOCKET)
266 except NoMasterError:
267 ss = ssconf.SimpleStore()
268
269 # Try to read ssconf file
270 try:
271 ss.GetMasterNode()
272 except errors.ConfigurationError:
273 raise errors.OpPrereqError("Cluster not initialized or this machine is"
274 " not part of a cluster",
275 errors.ECODE_INVAL)
276
277 master, myself = ssconf.GetMasterAndMyself(ss=ss)
278 if master != myself:
279 raise errors.OpPrereqError("This is not the master node, please connect"
280 " to node '%s' and rerun the command" %
281 master, errors.ECODE_INVAL)
282 raise
283 return client