runtime.py 8.01 KB
Newer Older
1
#
Michael Hanselmann's avatar
Michael Hanselmann committed
2
#
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

# Copyright (C) 2010 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.

"""Module implementing configuration details at runtime.

"""


import grp
import pwd
import threading
29
import platform
30 31 32

from ganeti import constants
from ganeti import errors
33
from ganeti import luxi
34
from ganeti.rpc.errors import NoMasterError
35 36
from ganeti import pathutils
from ganeti import ssconf
37
from ganeti import utils
38 39 40 41 42


_priv = None
_priv_lock = threading.Lock()

43 44 45
#: Architecture information
_arch = None

46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81

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

  @type user: string
  @param user: The username to retrieve
  @return: The resolved uid

  """
  try:
    return _getpwnam(user).pw_uid
  except KeyError, err:
    raise errors.ConfigurationError("User '%s' not found (%s)" % (user, err))


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

  @type group: string
  @param group: The group name to retrieve
  @return: The resolved gid

  """
  try:
    return _getgrnam(group).gr_gid
  except KeyError, err:
    raise errors.ConfigurationError("Group '%s' not found (%s)" % (group, err))


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

  @ivar masterd_uid: The resolved uid of the masterd user
  @ivar masterd_gid: The resolved gid of the masterd group
  @ivar confd_uid: The resolved uid of the confd user
  @ivar confd_gid: The resolved gid of the confd group
82 83
  @ivar wconfd_uid: The resolved uid of the wconfd user
  @ivar wconfd_gid: The resolved gid of the wconfd group
Thomas Thrainer's avatar
Thomas Thrainer committed
84 85
  @ivar luxid_uid: The resolved uid of the luxid user
  @ivar luxid_gid: The resolved gid of the luxid group
86 87 88 89 90
  @ivar rapi_uid: The resolved uid of the rapi user
  @ivar rapi_gid: The resolved gid of the rapi group
  @ivar noded_uid: The resolved uid of the noded user
  @ivar daemons_gid: The resolved gid of the daemons group
  @ivar admin_gid: The resolved gid of the admin group
91

92 93 94 95 96 97 98 99 100 101 102 103
  """
  def __init__(self, _getpwnam=pwd.getpwnam, _getgrnam=grp.getgrnam):
    """Initialize the resolver.

    """
    # Daemon pairs
    self.masterd_uid = GetUid(constants.MASTERD_USER, _getpwnam)
    self.masterd_gid = GetGid(constants.MASTERD_GROUP, _getgrnam)

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

104 105 106
    self.wconfd_uid = GetUid(constants.WCONFD_USER, _getpwnam)
    self.wconfd_gid = GetGid(constants.WCONFD_GROUP, _getgrnam)

Thomas Thrainer's avatar
Thomas Thrainer committed
107 108
    self.luxid_uid = GetUid(constants.LUXID_USER, _getpwnam)
    self.luxid_gid = GetGid(constants.LUXID_GROUP, _getgrnam)
109

110 111 112 113
    self.rapi_uid = GetUid(constants.RAPI_USER, _getpwnam)
    self.rapi_gid = GetGid(constants.RAPI_GROUP, _getgrnam)

    self.noded_uid = GetUid(constants.NODED_USER, _getpwnam)
114
    self.noded_gid = GetGid(constants.NODED_GROUP, _getgrnam)
115

116 117 118
    self.mond_uid = GetUid(constants.MOND_USER, _getpwnam)
    self.mond_gid = GetGid(constants.MOND_GROUP, _getgrnam)

119 120 121 122
    # Misc Ganeti groups
    self.daemons_gid = GetGid(constants.DAEMONS_GROUP, _getgrnam)
    self.admin_gid = GetGid(constants.ADMIN_GROUP, _getgrnam)

123 124 125
    self._uid2user = {
      self.masterd_uid: constants.MASTERD_USER,
      self.confd_uid: constants.CONFD_USER,
126
      self.wconfd_uid: constants.WCONFD_USER,
Thomas Thrainer's avatar
Thomas Thrainer committed
127
      self.luxid_uid: constants.LUXID_USER,
128 129
      self.rapi_uid: constants.RAPI_USER,
      self.noded_uid: constants.NODED_USER,
130
      self.mond_uid: constants.MOND_USER,
131 132 133 134 135
      }

    self._gid2group = {
      self.masterd_gid: constants.MASTERD_GROUP,
      self.confd_gid: constants.CONFD_GROUP,
136
      self.wconfd_gid: constants.WCONFD_GROUP,
Thomas Thrainer's avatar
Thomas Thrainer committed
137
      self.luxid_gid: constants.LUXID_GROUP,
138 139
      self.rapi_gid: constants.RAPI_GROUP,
      self.noded_gid: constants.NODED_GROUP,
140
      self.mond_gid: constants.MOND_GROUP,
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
      self.daemons_gid: constants.DAEMONS_GROUP,
      self.admin_gid: constants.ADMIN_GROUP,
      }

    self._user2uid = utils.InvertDict(self._uid2user)
    self._group2gid = utils.InvertDict(self._gid2group)

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

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

    """
    try:
      return self._uid2user[uid]
    except KeyError:
      raise errors.ConfigurationError("Unknown Ganeti uid '%d'" % uid)

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

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

    """
    try:
      return self._gid2group[gid]
    except KeyError:
      raise errors.ConfigurationError("Unknown Ganeti gid '%d'" % gid)

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

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

    """
    try:
      return self._user2uid[name]
    except KeyError:
      raise errors.ConfigurationError("Unknown Ganeti user '%s'" % name)

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

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

    """
    try:
      return self._group2gid[name]
    except KeyError:
      raise errors.ConfigurationError("Unknown Ganeti group '%s'" % name)

196 197 198 199 200

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

  As this method is accessed by multiple threads at the same time
201
  we need to take thread-safety carefully.
202 203 204

  """
  # We need to use the global keyword here
205
  global _priv # pylint: disable=W0603
206 207 208 209 210 211

  if not _priv:
    _priv_lock.acquire()
    try:
      if not _priv:
        # W0621: Redefine '_priv' from outer scope (used for singleton)
212
        _priv = resolver() # pylint: disable=W0621
213 214 215 216
    finally:
      _priv_lock.release()

  return _priv
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248


def InitArchInfo():
  """Initialize architecture information.

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

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

  """
  global _arch # pylint: disable=W0603

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

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


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

  """
  if _arch is None:
    raise errors.ProgrammerError("Architecture information hasn't been"
                                 " initialized")

  return _arch
249 250


251
def GetClient():
252 253 254 255
  """Connects to the a luxi socket and returns a client.

  """
  try:
256
    client = luxi.Client(address=pathutils.QUERY_SOCKET)
257
  except NoMasterError:
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
    ss = ssconf.SimpleStore()

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

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