提交 8d6ad5c5 编写于 作者: R Radek Duda 提交者: Lukáš Doktor

service.py: multi-access safe call - bugfix

If `service_manager` is called multiple times, its previous instance is
used. The problem arises if different runner is used. Because it leads
to situation when service commands are passed to wrong VM or service is
run as systemd insted of SysVinit or vice versa. Global variables,
where information from previous run were stored were thus removed as well as
then unused functions. Selftest for two different calls was also added.

Steps to invoke:

```
ssn_c = vm_client.vm.wait_for_login()
runner = remote.RemoteRunner(session=ssn_c)
vdagentd = service.specific_service_manager("spice-vdagentd", run=runner.run)
vdagentd.stop() #  send to vm_client
ssn_g = vm_guest.vm.wait_for_login()
runner = remote.RemoteRunner(session=ssn_g)
vdagentd = service.specific_service_manager("spice-vdagentd", run=runner.run)
vdagentd.stop() #  send to vm_client (again!)
```

As a trade-off this may affect speed when invoked in a loop since
`service_manager` won't be cached any more.
Signed-off-by: NRadek Duda <rduda@redhat.com>
上级 f88dbb02
......@@ -395,7 +395,7 @@ class _ServiceCommandGenerator(object):
setattr(self, command, command_generator(command))
def _get_name_of_init(run=process.run):
def get_name_of_init(run=process.run):
"""
Internal function to determine what executable is PID 1
......@@ -423,25 +423,6 @@ def _get_name_of_init(run=process.run):
return os.path.basename(output)
def get_name_of_init(run=process.run):
"""
Determine what executable is PID 1, aka init by checking /proc/1/exe
This init detection will only run once and cache the return value.
:return: executable name for PID 1, aka init
:rtype: str
"""
# _init_name is explicitly undefined so that we get the NameError on
# first access
# pylint: disable=W0601
global _init_name
try:
return _init_name
except (NameError, AttributeError):
_init_name = _get_name_of_init(run)
return _init_name
class _SpecificServiceManager(object):
def __init__(self, service_name, service_command_generator,
......@@ -709,44 +690,6 @@ _service_managers = {"init": _SysVInitServiceManager,
"systemd": _SystemdServiceManager}
def _get_service_result_parser(run=process.run):
"""
Get the ServiceResultParser using the auto-detect init command.
:return: ServiceResultParser fro the current init command.
:rtype: _ServiceResultParser
"""
# pylint: disable=W0601
global _service_result_parser
try:
return _service_result_parser
except NameError:
result_parser = _result_parsers[get_name_of_init(run)]
_service_result_parser = _ServiceResultParser(result_parser)
return _service_result_parser
def _get_service_command_generator(run=process.run):
"""
Lazy initializer for ServiceCommandGenerator using the auto-detect init
command.
:return: ServiceCommandGenerator for the current init command.
:rtype: _ServiceCommandGenerator
"""
# service_command_generator is explicitly undefined so that we get the
# NameError on first access
# pylint: disable=W0601
global _service_command_generator
try:
return _service_command_generator
except NameError:
command_generator = _command_generators[get_name_of_init(run)]
_service_command_generator = _ServiceCommandGenerator(
command_generator)
return _service_command_generator
def service_manager(run=process.run):
"""
Detect which init program is being used, init or systemd and return a
......@@ -771,20 +714,16 @@ def service_manager(run=process.run):
:return: SysVInitServiceManager or SystemdServiceManager
:rtype: _GenericServiceManager
"""
internal_service_manager = _service_managers[get_name_of_init(run)]
# service_command_generator is explicitly undefined so that we get the
# NameError on first access
# pylint: disable=W0601
global _service_manager
try:
return _service_manager
except NameError:
internal_generator = _get_service_command_generator
internal_parser = _get_service_result_parser
_service_manager = internal_service_manager(internal_generator(run),
internal_parser(run),
run=run)
return _service_manager
init = get_name_of_init(run)
internal_service_manager = _service_managers[init]
internal_command_generator = _command_generators[init]
internal_result_parser = _result_parsers[init]
internal_generator = _ServiceCommandGenerator(internal_command_generator)
internal_parser = _ServiceResultParser(internal_result_parser)
_service_manager = internal_service_manager(internal_generator,
internal_parser, run=run)
return _service_manager
ServiceManager = service_manager
......@@ -844,10 +783,12 @@ def specific_service_manager(service_name, run=process.run):
:return: SpecificServiceManager that has start/stop methods
:rtype: _SpecificServiceManager
"""
init = get_name_of_init(run)
result_parser = _result_parsers[init]
specific_generator = _auto_create_specific_service_command_generator
return _SpecificServiceManager(service_name,
specific_generator(run),
_get_service_result_parser(run), run)
_ServiceResultParser(result_parser), run)
SpecificServiceManager = specific_service_manager
......@@ -26,6 +26,30 @@ except ImportError:
from avocado.utils import service
class TestRunCalls(unittest.TestCase):
def setUp(self):
self.run_param1 = ["foo_target", "set_target", mock.Mock()]
self.run_param2 = ["foo_service", "start", mock.Mock()]
self.run_param1[-1].return_value.stdout = "systemd"
self.run_param2[-1].return_value.stdout = "init"
self.results = ["systemctl isolate foo_target",
"service foo_service start"]
def test_run_calls(self):
def run_call(run_params):
run_mock = run_params[-1]
serv = service.service_manager(run=run_mock)
self.assertTrue(run_mock.called)
getattr(serv, run_params[1])(run_params[0])
for test_params, test_results in zip([self.run_param1, self.run_param2],
self.results):
run_call(test_params)
self.assertEqual(test_params[-1].call_args[0][0], test_results)
class TestSystemd(unittest.TestCase):
def setUp(self):
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册