Statistics
| Branch: | Tag: | Revision:

root / lib / runtime.py @ a51b19de

History | View | Annotate | Download (8.3 kB)

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 os
30
import platform
31

    
32
from ganeti import constants
33
from ganeti import errors
34
from ganeti import luxi
35
from ganeti import pathutils
36
from ganeti import ssconf
37
from ganeti import utils
38

    
39

    
40
_priv = None
41
_priv_lock = threading.Lock()
42

    
43
#: Architecture information
44
_arch = None
45

    
46

    
47
def GetUid(user, _getpwnam):
48
  """Retrieve the uid from the database.
49

50
  @type user: string
51
  @param user: The username to retrieve
52
  @return: The resolved uid
53

54
  """
55
  try:
56
    return _getpwnam(user).pw_uid
57
  except KeyError, err:
58
    raise errors.ConfigurationError("User '%s' not found (%s)" % (user, err))
59

    
60

    
61
def GetGid(group, _getgrnam):
62
  """Retrieve the gid from the database.
63

64
  @type group: string
65
  @param group: The group name to retrieve
66
  @return: The resolved gid
67

68
  """
69
  try:
70
    return _getgrnam(group).gr_gid
71
  except KeyError, err:
72
    raise errors.ConfigurationError("Group '%s' not found (%s)" % (group, err))
73

    
74

    
75
class GetentResolver:
76
  """Resolves Ganeti uids and gids by name.
77

78
  @ivar masterd_uid: The resolved uid of the masterd user
79
  @ivar masterd_gid: The resolved gid of the masterd group
80
  @ivar confd_uid: The resolved uid of the confd user
81
  @ivar confd_gid: The resolved gid of the confd group
82
  @ivar luxid_uid: The resolved uid of the luxid user
83
  @ivar luxid_gid: The resolved gid of the luxid group
84
  @ivar rapi_uid: The resolved uid of the rapi user
85
  @ivar rapi_gid: The resolved gid of the rapi group
86
  @ivar noded_uid: The resolved uid of the noded user
87
  @ivar daemons_gid: The resolved gid of the daemons group
88
  @ivar admin_gid: The resolved gid of the admin group
89

90
  """
91
  def __init__(self, _getpwnam=pwd.getpwnam, _getgrnam=grp.getgrnam):
92
    """Initialize the resolver.
93

94
    """
95
    # Daemon pairs
96
    self.masterd_uid = GetUid(constants.MASTERD_USER, _getpwnam)
97
    self.masterd_gid = GetGid(constants.MASTERD_GROUP, _getgrnam)
98

    
99
    self.confd_uid = GetUid(constants.CONFD_USER, _getpwnam)
100
    self.confd_gid = GetGid(constants.CONFD_GROUP, _getgrnam)
101

    
102
    self.luxid_uid = GetUid(constants.LUXID_USER, _getpwnam)
103
    self.luxid_gid = GetGid(constants.LUXID_GROUP, _getgrnam)
104

    
105
    self.rapi_uid = GetUid(constants.RAPI_USER, _getpwnam)
106
    self.rapi_gid = GetGid(constants.RAPI_GROUP, _getgrnam)
107

    
108
    self.noded_uid = GetUid(constants.NODED_USER, _getpwnam)
109
    self.noded_gid = GetGid(constants.NODED_GROUP, _getgrnam)
110

    
111
    self.mond_uid = GetUid(constants.MOND_USER, _getpwnam)
112
    self.mond_gid = GetGid(constants.MOND_GROUP, _getgrnam)
113

    
114
    # Misc Ganeti groups
115
    self.daemons_gid = GetGid(constants.DAEMONS_GROUP, _getgrnam)
116
    self.admin_gid = GetGid(constants.ADMIN_GROUP, _getgrnam)
117

    
118
    self._uid2user = {
119
      self.masterd_uid: constants.MASTERD_USER,
120
      self.confd_uid: constants.CONFD_USER,
121
      self.luxid_uid: constants.LUXID_USER,
122
      self.rapi_uid: constants.RAPI_USER,
123
      self.noded_uid: constants.NODED_USER,
124
      self.mond_uid: constants.MOND_USER,
125
      }
126

    
127
    self._gid2group = {
128
      self.masterd_gid: constants.MASTERD_GROUP,
129
      self.confd_gid: constants.CONFD_GROUP,
130
      self.luxid_gid: constants.LUXID_GROUP,
131
      self.rapi_gid: constants.RAPI_GROUP,
132
      self.noded_gid: constants.NODED_GROUP,
133
      self.mond_gid: constants.MOND_GROUP,
134
      self.daemons_gid: constants.DAEMONS_GROUP,
135
      self.admin_gid: constants.ADMIN_GROUP,
136
      }
137

    
138
    self._user2uid = utils.InvertDict(self._uid2user)
139
    self._group2gid = utils.InvertDict(self._gid2group)
140

    
141
  def LookupUid(self, uid):
142
    """Looks which Ganeti user belongs to this uid.
143

144
    @param uid: The uid to lookup
145
    @returns The user name associated with that uid
146

147
    """
148
    try:
149
      return self._uid2user[uid]
150
    except KeyError:
151
      raise errors.ConfigurationError("Unknown Ganeti uid '%d'" % uid)
152

    
153
  def LookupGid(self, gid):
154
    """Looks which Ganeti group belongs to this gid.
155

156
    @param gid: The gid to lookup
157
    @returns The group name associated with that gid
158

159
    """
160
    try:
161
      return self._gid2group[gid]
162
    except KeyError:
163
      raise errors.ConfigurationError("Unknown Ganeti gid '%d'" % gid)
164

    
165
  def LookupUser(self, name):
166
    """Looks which uid belongs to this name.
167

168
    @param name: The name to lookup
169
    @returns The uid associated with that user name
170

171
    """
172
    try:
173
      return self._user2uid[name]
174
    except KeyError:
175
      raise errors.ConfigurationError("Unknown Ganeti user '%s'" % name)
176

    
177
  def LookupGroup(self, name):
178
    """Looks which gid belongs to this name.
179

180
    @param name: The name to lookup
181
    @returns The gid associated with that group name
182

183
    """
184
    try:
185
      return self._group2gid[name]
186
    except KeyError:
187
      raise errors.ConfigurationError("Unknown Ganeti group '%s'" % name)
188

    
189

    
190
def GetEnts(resolver=GetentResolver):
191
  """Singleton wrapper around resolver instance.
192

193
  As this method is accessed by multiple threads at the same time
194
  we need to take thread-safety carefully.
195

196
  """
197
  # We need to use the global keyword here
198
  global _priv # pylint: disable=W0603
199

    
200
  if not _priv:
201
    _priv_lock.acquire()
202
    try:
203
      if not _priv:
204
        # W0621: Redefine '_priv' from outer scope (used for singleton)
205
        _priv = resolver() # pylint: disable=W0621
206
    finally:
207
      _priv_lock.release()
208

    
209
  return _priv
210

    
211

    
212
def InitArchInfo():
213
  """Initialize architecture information.
214

215
  We can assume this information never changes during the lifetime of a
216
  process, therefore the information can easily be cached.
217

218
  @note: This function uses C{platform.architecture} to retrieve the Python
219
    binary architecture and does so by forking to run C{file} (see Python
220
    documentation for more information). Therefore it must not be used in a
221
    multi-threaded environment.
222

223
  """
224
  global _arch # pylint: disable=W0603
225

    
226
  if _arch is not None:
227
    raise errors.ProgrammerError("Architecture information can only be"
228
                                 " initialized once")
229

    
230
  _arch = (platform.architecture()[0], platform.machine())
231

    
232

    
233
def GetArchInfo():
234
  """Returns previsouly initialized architecture information.
235

236
  """
237
  if _arch is None:
238
    raise errors.ProgrammerError("Architecture information hasn't been"
239
                                 " initialized")
240

    
241
  return _arch
242

    
243

    
244
def GetClient(query=False):
245
  """Connects to the a luxi socket and returns a client.
246

247
  @type query: boolean
248
  @param query: this signifies that the client will only be
249
      used for queries; if the build-time parameter
250
      enable-split-queries is enabled, then the client will be
251
      connected to the query socket instead of the masterd socket
252

253
  """
254
  override_socket = os.getenv(constants.LUXI_OVERRIDE, "")
255
  if override_socket:
256
    if override_socket == constants.LUXI_OVERRIDE_MASTER:
257
      address = pathutils.MASTER_SOCKET
258
    elif override_socket == constants.LUXI_OVERRIDE_QUERY:
259
      address = pathutils.QUERY_SOCKET
260
    else:
261
      address = override_socket
262
  elif query:
263
    address = pathutils.QUERY_SOCKET
264
  else:
265
    address = None
266
  # TODO: Cache object?
267
  try:
268
    client = luxi.Client(address=address)
269
  except luxi.NoMasterError:
270
    ss = ssconf.SimpleStore()
271

    
272
    # Try to read ssconf file
273
    try:
274
      ss.GetMasterNode()
275
    except errors.ConfigurationError:
276
      raise errors.OpPrereqError("Cluster not initialized or this machine is"
277
                                 " not part of a cluster",
278
                                 errors.ECODE_INVAL)
279

    
280
    master, myself = ssconf.GetMasterAndMyself(ss=ss)
281
    if master != myself:
282
      raise errors.OpPrereqError("This is not the master node, please connect"
283
                                 " to node '%s' and rerun the command" %
284
                                 master, errors.ECODE_INVAL)
285
    raise
286
  return client