Statistics
| Branch: | Tag: | Revision:

root / lib / runtime.py @ 912b2278

History | View | Annotate | Download (8.4 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.rpc.errors import NoMasterError
36
from ganeti import pathutils
37
from ganeti import ssconf
38
from ganeti import utils
39

    
40

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

    
44
#: Architecture information
45
_arch = None
46

    
47

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

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

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

    
61

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

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

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

    
75

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
190

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

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

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

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

    
210
  return _priv
211

    
212

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

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

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

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

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

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

    
233

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

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

    
242
  return _arch
243

    
244

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

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

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

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

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