Commit bd614fa4 authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

Merge branch 'devel-2.4'



* devel-2.4:
  RPC/Backend: Make UploadFile uid and gid agnostic
  Resolve uid/gid upon mainloop run
  GetEntResolver: Make it possible to resolve uid/gid to name
  utils.algo: Add InvertDict to invert a dict
  autotools: Add noded group
Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parents 3f1e065d 9a914f7a
......@@ -876,6 +876,7 @@ lib/_autoconf.py: Makefile vcs-version | lib/.dir
echo "CONFD_USER = '$(CONFD_USER)'"; \
echo "CONFD_GROUP = '$(CONFD_GROUP)'"; \
echo "NODED_USER = '$(NODED_USER)'"; \
echo "NODED_GROUP = '$(NODED_GROUP)'"; \
echo "VCS_VERSION = '$$VCSVER'"; \
echo "DISK_SEPARATOR = '$(DISK_SEPARATOR)'"; \
if [ "$(HTOOLS)" ]; then \
......
......@@ -183,16 +183,19 @@ AC_ARG_WITH([group-prefix],
group_admin="${withval}admin";
group_confd="${withval}confd";
group_masterd="${withval}masterd";
group_noded="root";
group_daemons="${withval}daemons";],
[group_rapi="root";
group_admin="root";
group_confd="root";
group_masterd="root";
group_noded="root";
group_daemons="root"])
AC_SUBST(RAPI_GROUP, $group_rapi)
AC_SUBST(ADMIN_GROUP, $group_admin)
AC_SUBST(CONFD_GROUP, $group_confd)
AC_SUBST(MASTERD_GROUP, $group_masterd)
AC_SUBST(NODED_GROUP, $group_noded)
AC_SUBST(DAEMONS_GROUP, $group_daemons)
# Print the config to the user
......
......@@ -1850,10 +1850,10 @@ def UploadFile(file_name, data, mode, uid, gid, atime, mtime):
@param data: the new contents of the file
@type mode: int
@param mode: the mode to give the file (can be None)
@type uid: int
@param uid: the owner of the file (can be -1 for default)
@type gid: int
@param gid: the group of the file (can be -1 for default)
@type uid: string
@param uid: the owner of the file
@type gid: string
@param gid: the group of the file
@type atime: float
@param atime: the atime to set on the file (can be None)
@type mtime: float
......@@ -1870,6 +1870,13 @@ def UploadFile(file_name, data, mode, uid, gid, atime, mtime):
raw_data = _Decompress(data)
if not (isinstance(uid, basestring) and isinstance(gid, basestring)):
_Fail("Invalid username/groupname type")
getents = runtime.GetEnts()
uid = getents.LookupUser(uid)
gid = getents.LookupGroup(gid)
utils.SafeWriteFile(file_name, None,
data=raw_data, mode=mode, uid=uid, gid=gid,
atime=atime, mtime=mtime)
......
......@@ -96,6 +96,7 @@ RAPI_GROUP = _autoconf.RAPI_GROUP
CONFD_USER = _autoconf.CONFD_USER
CONFD_GROUP = _autoconf.CONFD_GROUP
NODED_USER = _autoconf.NODED_USER
NODED_GROUP = _autoconf.NODED_GROUP
# Wipe
......
......@@ -435,6 +435,9 @@ class Mainloop(object):
self._signal_wait = []
self.scheduler = AsyncoreScheduler(time.time)
# Resolve uid/gids used
runtime.GetEnts()
@utils.SignalHandled([signal.SIGCHLD])
@utils.SignalHandled([signal.SIGTERM])
@utils.SignalHandled([signal.SIGINT])
......@@ -449,6 +452,7 @@ class Mainloop(object):
len(signal_handlers) > 0, \
"Broken SignalHandled decorator"
running = True
# Start actual main loop
while running:
if not self.scheduler.empty():
......
......@@ -45,6 +45,7 @@ from ganeti import constants
from ganeti import errors
from ganeti import netutils
from ganeti import ssconf
from ganeti import runtime
# pylint has a bug here, doesn't see this import
import ganeti.http.client # pylint: disable-msg=W0611
......@@ -1177,8 +1178,9 @@ class RpcRunner(object):
file_contents = utils.ReadFile(file_name)
data = cls._Compress(file_contents)
st = os.stat(file_name)
params = [file_name, data, st.st_mode, st.st_uid, st.st_gid,
st.st_atime, st.st_mtime]
getents = runtime.GetEnts()
params = [file_name, data, st.st_mode, getents.LookupUid(st.st_uid),
getents.LookupGid(st.st_gid), st.st_atime, st.st_mtime]
return cls._StaticMultiNodeCall(node_list, "upload_file", params,
address_list=address_list)
......
......@@ -28,6 +28,7 @@ import threading
from ganeti import constants
from ganeti import errors
from ganeti import utils
_priv = None
......@@ -91,11 +92,79 @@ class GetentResolver:
self.rapi_gid = GetGid(constants.RAPI_GROUP, _getgrnam)
self.noded_uid = GetUid(constants.NODED_USER, _getpwnam)
self.noded_gid = GetGid(constants.NODED_GROUP, _getgrnam)
# Misc Ganeti groups
self.daemons_gid = GetGid(constants.DAEMONS_GROUP, _getgrnam)
self.admin_gid = GetGid(constants.ADMIN_GROUP, _getgrnam)
self._uid2user = {
self.masterd_uid: constants.MASTERD_USER,
self.confd_uid: constants.CONFD_USER,
self.rapi_uid: constants.RAPI_USER,
self.noded_uid: constants.NODED_USER,
}
self._gid2group = {
self.masterd_gid: constants.MASTERD_GROUP,
self.confd_gid: constants.CONFD_GROUP,
self.rapi_gid: constants.RAPI_GROUP,
self.noded_gid: constants.NODED_GROUP,
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)
def GetEnts(resolver=GetentResolver):
"""Singleton wrapper around resolver instance.
......
......@@ -115,6 +115,16 @@ def NiceSort(values, key=None):
return sorted(values, key=keyfunc)
def InvertDict(dict_in):
"""Inverts the key/value mapping of a dict.
@param dict_in: The dict to invert
@returns the inverted dict
"""
return dict(zip(dict_in.values(), dict_in.keys()))
class RunningTimeout(object):
"""Class to calculate remaining timeout when doing several operations.
......
......@@ -25,6 +25,7 @@ from ganeti import errors
from ganeti import runtime
import testutils
import unittest
class _EntStub:
......@@ -50,6 +51,7 @@ def _StubGetgrnam(group):
constants.RAPI_GROUP: _EntStub(gid=2),
constants.DAEMONS_GROUP: _EntStub(gid=3),
constants.ADMIN_GROUP: _EntStub(gid=4),
constants.NODED_GROUP: _EntStub(gid=5),
}
return groups[group]
......@@ -67,29 +69,30 @@ class ResolverStubRaising(object):
raise errors.ConfigurationError("No entries")
class TestErrors(testutils.GanetiTestCase):
def testEverythingSuccessful(self):
resolver = runtime.GetentResolver(_getpwnam=_StubGetpwnam,
_getgrnam=_StubGetgrnam)
class TestErrors(unittest.TestCase):
def setUp(self):
self.resolver = runtime.GetentResolver(_getpwnam=_StubGetpwnam,
_getgrnam=_StubGetgrnam)
self.assertEqual(resolver.masterd_uid,
def testEverythingSuccessful(self):
self.assertEqual(self.resolver.masterd_uid,
_StubGetpwnam(constants.MASTERD_USER).pw_uid)
self.assertEqual(resolver.masterd_gid,
self.assertEqual(self.resolver.masterd_gid,
_StubGetgrnam(constants.MASTERD_GROUP).gr_gid)
self.assertEqual(resolver.confd_uid,
self.assertEqual(self.resolver.confd_uid,
_StubGetpwnam(constants.CONFD_USER).pw_uid)
self.assertEqual(resolver.confd_gid,
self.assertEqual(self.resolver.confd_gid,
_StubGetgrnam(constants.CONFD_GROUP).gr_gid)
self.assertEqual(resolver.rapi_uid,
self.assertEqual(self.resolver.rapi_uid,
_StubGetpwnam(constants.RAPI_USER).pw_uid)
self.assertEqual(resolver.rapi_gid,
self.assertEqual(self.resolver.rapi_gid,
_StubGetgrnam(constants.RAPI_GROUP).gr_gid)
self.assertEqual(resolver.noded_uid,
self.assertEqual(self.resolver.noded_uid,
_StubGetpwnam(constants.NODED_USER).pw_uid)
self.assertEqual(resolver.daemons_gid,
self.assertEqual(self.resolver.daemons_gid,
_StubGetgrnam(constants.DAEMONS_GROUP).gr_gid)
self.assertEqual(resolver.admin_gid,
self.assertEqual(self.resolver.admin_gid,
_StubGetgrnam(constants.ADMIN_GROUP).gr_gid)
def testUserNotFound(self):
......@@ -104,6 +107,36 @@ class TestErrors(testutils.GanetiTestCase):
self.assertRaises(errors.ConfigurationError, runtime.GetEnts,
resolver=ResolverStubRaising)
def testLookupForUser(self):
master_stub = _StubGetpwnam(constants.MASTERD_USER)
rapi_stub = _StubGetpwnam(constants.RAPI_USER)
self.assertEqual(self.resolver.LookupUid(master_stub.pw_uid),
constants.MASTERD_USER)
self.assertEqual(self.resolver.LookupUid(rapi_stub.pw_uid),
constants.RAPI_USER)
self.assertEqual(self.resolver.LookupUser(constants.MASTERD_USER),
master_stub.pw_uid)
self.assertEqual(self.resolver.LookupUser(constants.RAPI_USER),
rapi_stub.pw_uid)
def testLookupForGroup(self):
master_stub = _StubGetgrnam(constants.MASTERD_GROUP)
rapi_stub = _StubGetgrnam(constants.RAPI_GROUP)
self.assertEqual(self.resolver.LookupGid(master_stub.gr_gid),
constants.MASTERD_GROUP)
self.assertEqual(self.resolver.LookupGid(rapi_stub.gr_gid),
constants.RAPI_GROUP)
def testLookupForUserNotFound(self):
self.assertRaises(errors.ConfigurationError, self.resolver.LookupUid, 9999)
self.assertRaises(errors.ConfigurationError,
self.resolver.LookupUser, "does-not-exist-foo")
def testLookupForGroupNotFound(self):
self.assertRaises(errors.ConfigurationError, self.resolver.LookupGid, 9999)
self.assertRaises(errors.ConfigurationError,
self.resolver.LookupGroup, "does-not-exist-foo")
if __name__ == "__main__":
testutils.GanetiTestProgram()
......@@ -229,6 +229,13 @@ class TestNiceSort(unittest.TestCase):
None, ""])
class TestInvertDict(unittest.TestCase):
def testInvertDict(self):
test_dict = { "foo": 1, "bar": 2, "baz": 5 }
self.assertEqual(algo.InvertDict(test_dict),
{ 1: "foo", 2: "bar", 5: "baz"})
class TimeMock:
def __init__(self, values):
self.values = values
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment