Merge branch 'devel-2.5'
[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 rapi_uid: The resolved uid of the rapi user
79   @ivar rapi_gid: The resolved gid of the rapi group
80   @ivar noded_uid: The resolved uid of the noded user
81   @ivar daemons_gid: The resolved gid of the daemons group
82   @ivar admin_gid: The resolved gid of the admin group
83
84   """
85   def __init__(self, _getpwnam=pwd.getpwnam, _getgrnam=grp.getgrnam):
86     """Initialize the resolver.
87
88     """
89     # Daemon pairs
90     self.masterd_uid = GetUid(constants.MASTERD_USER, _getpwnam)
91     self.masterd_gid = GetGid(constants.MASTERD_GROUP, _getgrnam)
92
93     self.confd_uid = GetUid(constants.CONFD_USER, _getpwnam)
94     self.confd_gid = GetGid(constants.CONFD_GROUP, _getgrnam)
95
96     self.rapi_uid = GetUid(constants.RAPI_USER, _getpwnam)
97     self.rapi_gid = GetGid(constants.RAPI_GROUP, _getgrnam)
98
99     self.noded_uid = GetUid(constants.NODED_USER, _getpwnam)
100     self.noded_gid = GetGid(constants.NODED_GROUP, _getgrnam)
101
102     # Misc Ganeti groups
103     self.daemons_gid = GetGid(constants.DAEMONS_GROUP, _getgrnam)
104     self.admin_gid = GetGid(constants.ADMIN_GROUP, _getgrnam)
105
106     self._uid2user = {
107       self.masterd_uid: constants.MASTERD_USER,
108       self.confd_uid: constants.CONFD_USER,
109       self.rapi_uid: constants.RAPI_USER,
110       self.noded_uid: constants.NODED_USER,
111       }
112
113     self._gid2group = {
114       self.masterd_gid: constants.MASTERD_GROUP,
115       self.confd_gid: constants.CONFD_GROUP,
116       self.rapi_gid: constants.RAPI_GROUP,
117       self.noded_gid: constants.NODED_GROUP,
118       self.daemons_gid: constants.DAEMONS_GROUP,
119       self.admin_gid: constants.ADMIN_GROUP,
120       }
121
122     self._user2uid = utils.InvertDict(self._uid2user)
123     self._group2gid = utils.InvertDict(self._gid2group)
124
125   def LookupUid(self, uid):
126     """Looks which Ganeti user belongs to this uid.
127
128     @param uid: The uid to lookup
129     @returns The user name associated with that uid
130
131     """
132     try:
133       return self._uid2user[uid]
134     except KeyError:
135       raise errors.ConfigurationError("Unknown Ganeti uid '%d'" % uid)
136
137   def LookupGid(self, gid):
138     """Looks which Ganeti group belongs to this gid.
139
140     @param gid: The gid to lookup
141     @returns The group name associated with that gid
142
143     """
144     try:
145       return self._gid2group[gid]
146     except KeyError:
147       raise errors.ConfigurationError("Unknown Ganeti gid '%d'" % gid)
148
149   def LookupUser(self, name):
150     """Looks which uid belongs to this name.
151
152     @param name: The name to lookup
153     @returns The uid associated with that user name
154
155     """
156     try:
157       return self._user2uid[name]
158     except KeyError:
159       raise errors.ConfigurationError("Unknown Ganeti user '%s'" % name)
160
161   def LookupGroup(self, name):
162     """Looks which gid belongs to this name.
163
164     @param name: The name to lookup
165     @returns The gid associated with that group name
166
167     """
168     try:
169       return self._group2gid[name]
170     except KeyError:
171       raise errors.ConfigurationError("Unknown Ganeti group '%s'" % name)
172
173
174 def GetEnts(resolver=GetentResolver):
175   """Singleton wrapper around resolver instance.
176
177   As this method is accessed by multiple threads at the same time
178   we need to take thread-safety carefully.
179
180   """
181   # We need to use the global keyword here
182   global _priv # pylint: disable=W0603
183
184   if not _priv:
185     _priv_lock.acquire()
186     try:
187       if not _priv:
188         # W0621: Redefine '_priv' from outer scope (used for singleton)
189         _priv = resolver() # pylint: disable=W0621
190     finally:
191       _priv_lock.release()
192
193   return _priv
194
195
196 def InitArchInfo():
197   """Initialize architecture information.
198
199   We can assume this information never changes during the lifetime of a
200   process, therefore the information can easily be cached.
201
202   @note: This function uses C{platform.architecture} to retrieve the Python
203     binary architecture and does so by forking to run C{file} (see Python
204     documentation for more information). Therefore it must not be used in a
205     multi-threaded environment.
206
207   """
208   global _arch # pylint: disable=W0603
209
210   if _arch is not None:
211     raise errors.ProgrammerError("Architecture information can only be"
212                                  " initialized once")
213
214   _arch = (platform.architecture()[0], platform.machine())
215
216
217 def GetArchInfo():
218   """Returns previsouly initialized architecture information.
219
220   """
221   if _arch is None:
222     raise errors.ProgrammerError("Architecture information hasn't been"
223                                  " initialized")
224
225   return _arch