Merge branch 'stable-2.8' into stable-2.9
[ganeti-local] / lib / runtime.py
1 #
2 #
3
4 # Copyright (C) 2010 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 """Module implementing configuration details at runtime.
22
23 """
24
25
26 import grp
27 import pwd
28 import threading
29 import platform
30
31 from ganeti import constants
32 from ganeti import errors
33 from ganeti import utils
34
35
36 _priv = None
37 _priv_lock = threading.Lock()
38
39 #: Architecture information
40 _arch = None
41
42
43 def GetUid(user, _getpwnam):
44   """Retrieve the uid from the database.
45
46   @type user: string
47   @param user: The username to retrieve
48   @return: The resolved uid
49
50   """
51   try:
52     return _getpwnam(user).pw_uid
53   except KeyError, err:
54     raise errors.ConfigurationError("User '%s' not found (%s)" % (user, err))
55
56
57 def GetGid(group, _getgrnam):
58   """Retrieve the gid from the database.
59
60   @type group: string
61   @param group: The group name to retrieve
62   @return: The resolved gid
63
64   """
65   try:
66     return _getgrnam(group).gr_gid
67   except KeyError, err:
68     raise errors.ConfigurationError("Group '%s' not found (%s)" % (group, err))
69
70
71 class GetentResolver:
72   """Resolves Ganeti uids and gids by name.
73
74   @ivar masterd_uid: The resolved uid of the masterd user
75   @ivar masterd_gid: The resolved gid of the masterd group
76   @ivar confd_uid: The resolved uid of the confd user
77   @ivar confd_gid: The resolved gid of the confd group
78   @ivar luxid_uid: The resolved uid of the luxid user
79   @ivar luxid_gid: The resolved gid of the luxid group
80   @ivar rapi_uid: The resolved uid of the rapi user
81   @ivar rapi_gid: The resolved gid of the rapi group
82   @ivar noded_uid: The resolved uid of the noded user
83   @ivar daemons_gid: The resolved gid of the daemons group
84   @ivar admin_gid: The resolved gid of the admin group
85
86   """
87   def __init__(self, _getpwnam=pwd.getpwnam, _getgrnam=grp.getgrnam):
88     """Initialize the resolver.
89
90     """
91     # Daemon pairs
92     self.masterd_uid = GetUid(constants.MASTERD_USER, _getpwnam)
93     self.masterd_gid = GetGid(constants.MASTERD_GROUP, _getgrnam)
94
95     self.confd_uid = GetUid(constants.CONFD_USER, _getpwnam)
96     self.confd_gid = GetGid(constants.CONFD_GROUP, _getgrnam)
97
98     self.luxid_uid = GetUid(constants.LUXID_USER, _getpwnam)
99     self.luxid_gid = GetGid(constants.LUXID_GROUP, _getgrnam)
100
101     self.rapi_uid = GetUid(constants.RAPI_USER, _getpwnam)
102     self.rapi_gid = GetGid(constants.RAPI_GROUP, _getgrnam)
103
104     self.noded_uid = GetUid(constants.NODED_USER, _getpwnam)
105     self.noded_gid = GetGid(constants.NODED_GROUP, _getgrnam)
106
107     self.mond_uid = GetUid(constants.MOND_USER, _getpwnam)
108     self.mond_gid = GetGid(constants.MOND_GROUP, _getgrnam)
109
110     # Misc Ganeti groups
111     self.daemons_gid = GetGid(constants.DAEMONS_GROUP, _getgrnam)
112     self.admin_gid = GetGid(constants.ADMIN_GROUP, _getgrnam)
113
114     self._uid2user = {
115       self.masterd_uid: constants.MASTERD_USER,
116       self.confd_uid: constants.CONFD_USER,
117       self.luxid_uid: constants.LUXID_USER,
118       self.rapi_uid: constants.RAPI_USER,
119       self.noded_uid: constants.NODED_USER,
120       self.mond_uid: constants.MOND_USER,
121       }
122
123     self._gid2group = {
124       self.masterd_gid: constants.MASTERD_GROUP,
125       self.confd_gid: constants.CONFD_GROUP,
126       self.luxid_gid: constants.LUXID_GROUP,
127       self.rapi_gid: constants.RAPI_GROUP,
128       self.noded_gid: constants.NODED_GROUP,
129       self.mond_gid: constants.MOND_GROUP,
130       self.daemons_gid: constants.DAEMONS_GROUP,
131       self.admin_gid: constants.ADMIN_GROUP,
132       }
133
134     self._user2uid = utils.InvertDict(self._uid2user)
135     self._group2gid = utils.InvertDict(self._gid2group)
136
137   def LookupUid(self, uid):
138     """Looks which Ganeti user belongs to this uid.
139
140     @param uid: The uid to lookup
141     @returns The user name associated with that uid
142
143     """
144     try:
145       return self._uid2user[uid]
146     except KeyError:
147       raise errors.ConfigurationError("Unknown Ganeti uid '%d'" % uid)
148
149   def LookupGid(self, gid):
150     """Looks which Ganeti group belongs to this gid.
151
152     @param gid: The gid to lookup
153     @returns The group name associated with that gid
154
155     """
156     try:
157       return self._gid2group[gid]
158     except KeyError:
159       raise errors.ConfigurationError("Unknown Ganeti gid '%d'" % gid)
160
161   def LookupUser(self, name):
162     """Looks which uid belongs to this name.
163
164     @param name: The name to lookup
165     @returns The uid associated with that user name
166
167     """
168     try:
169       return self._user2uid[name]
170     except KeyError:
171       raise errors.ConfigurationError("Unknown Ganeti user '%s'" % name)
172
173   def LookupGroup(self, name):
174     """Looks which gid belongs to this name.
175
176     @param name: The name to lookup
177     @returns The gid associated with that group name
178
179     """
180     try:
181       return self._group2gid[name]
182     except KeyError:
183       raise errors.ConfigurationError("Unknown Ganeti group '%s'" % name)
184
185
186 def GetEnts(resolver=GetentResolver):
187   """Singleton wrapper around resolver instance.
188
189   As this method is accessed by multiple threads at the same time
190   we need to take thread-safety carefully.
191
192   """
193   # We need to use the global keyword here
194   global _priv # pylint: disable=W0603
195
196   if not _priv:
197     _priv_lock.acquire()
198     try:
199       if not _priv:
200         # W0621: Redefine '_priv' from outer scope (used for singleton)
201         _priv = resolver() # pylint: disable=W0621
202     finally:
203       _priv_lock.release()
204
205   return _priv
206
207
208 def InitArchInfo():
209   """Initialize architecture information.
210
211   We can assume this information never changes during the lifetime of a
212   process, therefore the information can easily be cached.
213
214   @note: This function uses C{platform.architecture} to retrieve the Python
215     binary architecture and does so by forking to run C{file} (see Python
216     documentation for more information). Therefore it must not be used in a
217     multi-threaded environment.
218
219   """
220   global _arch # pylint: disable=W0603
221
222   if _arch is not None:
223     raise errors.ProgrammerError("Architecture information can only be"
224                                  " initialized once")
225
226   _arch = (platform.architecture()[0], platform.machine())
227
228
229 def GetArchInfo():
230   """Returns previsouly initialized architecture information.
231
232   """
233   if _arch is None:
234     raise errors.ProgrammerError("Architecture information hasn't been"
235                                  " initialized")
236
237   return _arch