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

cmdlib: Stop forking in LUClusterQuery



While debugging another issue we realized that LUClusterQuery forks.
This turned out to be the “platform.architecture” function from the
Python library. It uses the “file” command to determine the architecture
of the Python binary.

This patch adds two new functions to the “runtime” module to get this
information once per process instead of doing it every single time
LUClusterQuery is used. Forking is a no-go in a multi-threaded
environment anyway.

A future change will also have to change the terminology in “gnt-cluster
info”: it reports the binary architecture simply as “architecture”, when
it's actually the binaries' architecture. Kernel and userland can be
different.
Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarBernardo Dal Seno <bdalseno@google.com>
parent 70567db0
......@@ -32,7 +32,6 @@ import os
import os.path
import time
import re
import platform
import logging
import copy
import OpenSSL
......@@ -59,6 +58,7 @@ from ganeti import query
from ganeti import qlang
from ganeti import opcodes
from ganeti import ht
from ganeti import runtime
import ganeti.masterd.instance # pylint: disable=W0611
......@@ -5520,7 +5520,7 @@ class LUClusterQuery(NoHooksLU):
"config_version": constants.CONFIG_VERSION,
"os_api_version": max(constants.OS_API_VERSIONS),
"export_version": constants.EXPORT_VERSION,
"architecture": (platform.architecture()[0], platform.machine()),
"architecture": runtime.GetArchInfo(),
"name": cluster.cluster_name,
"master": cluster.master_node,
"default_hypervisor": cluster.enabled_hypervisors[0],
......
......@@ -26,6 +26,7 @@
import grp
import pwd
import threading
import platform
from ganeti import constants
from ganeti import errors
......@@ -35,6 +36,9 @@ from ganeti import utils
_priv = None
_priv_lock = threading.Lock()
#: Architecture information
_arch = None
def GetUid(user, _getpwnam):
"""Retrieve the uid from the database.
......@@ -187,3 +191,35 @@ def GetEnts(resolver=GetentResolver):
_priv_lock.release()
return _priv
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
......@@ -57,6 +57,7 @@ from ganeti import bootstrap
from ganeti import netutils
from ganeti import objects
from ganeti import query
from ganeti import runtime
CLIENT_REQUEST_WORKERS = 16
......@@ -548,6 +549,9 @@ def CheckMasterd(options, args):
(constants.MASTERD_USER, constants.DAEMONS_GROUP))
sys.exit(constants.EXIT_FAILURE)
# Determine static runtime architecture information
runtime.InitArchInfo()
# Check the configuration is sane before anything else
try:
config.ConfigWriter()
......
......@@ -23,6 +23,7 @@
from ganeti import constants
from ganeti import errors
from ganeti import runtime
from ganeti import ht
import testutils
import unittest
......@@ -138,5 +139,37 @@ class TestErrors(unittest.TestCase):
self.resolver.LookupGroup, "does-not-exist-foo")
class TestArchInfo(unittest.TestCase):
EXP_TYPES = \
ht.TAnd(ht.TIsLength(2),
ht.TItems([
ht.TNonEmptyString,
ht.TNonEmptyString,
]))
def setUp(self):
self.assertTrue(runtime._arch is None)
def tearDown(self):
runtime._arch = None
def testNotInitialized(self):
self.assertRaises(errors.ProgrammerError, runtime.GetArchInfo)
def testInitializeMultiple(self):
runtime.InitArchInfo()
self.assertRaises(errors.ProgrammerError, runtime.InitArchInfo)
def testNormal(self):
runtime.InitArchInfo()
info = runtime.GetArchInfo()
self.assertTrue(self.EXP_TYPES(info),
msg=("Doesn't match expected type description: %s" %
self.EXP_TYPES))
if __name__ == "__main__":
testutils.GanetiTestProgram()
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