processes.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. # -*- coding: utf-8 -*-
  2. '''
  3. :copyright: Copyright 2017 by the SaltStack Team, see AUTHORS for more details.
  4. :license: Apache 2.0, see LICENSE for more details.
  5. tests.support.processes
  6. ~~~~~~~~~~~~~~~~~~~~~~~
  7. Process handling utilities
  8. '''
  9. # Import python libs
  10. from __future__ import absolute_import
  11. import logging
  12. # Import pytest-salt libs
  13. from pytestsalt.utils import SaltRunEventListener as PytestSaltRunEventListener
  14. from pytestsalt.utils import collect_child_processes, terminate_process, terminate_process_list # pylint: disable=unused-import
  15. from pytestsalt.fixtures.daemons import Salt as PytestSalt
  16. from pytestsalt.fixtures.daemons import SaltKey as PytestSaltKey
  17. from pytestsalt.fixtures.daemons import SaltRun as PytestSaltRun
  18. from pytestsalt.fixtures.daemons import SaltCall as PytestSaltCall
  19. from pytestsalt.fixtures.daemons import SaltMaster as PytestSaltMaster
  20. from pytestsalt.fixtures.daemons import SaltMinion as PytestSaltMinion
  21. from pytestsalt.fixtures.daemons import SaltSyndic as PytestSaltSyndic
  22. from pytestsalt.fixtures.daemons import SaltProxy as PytestSaltProxy
  23. # Import tests support libs
  24. from tests.support.paths import ScriptPathMixin
  25. log = logging.getLogger(__name__)
  26. class SaltRunEventListener(ScriptPathMixin, PytestSaltRunEventListener):
  27. '''
  28. Override this class's __init__ because there's no request argument since we're still
  29. not running under pytest
  30. '''
  31. class GetSaltRunFixtureMixin(ScriptPathMixin):
  32. '''
  33. Override this classes `get_salt_run_fixture` because we're still not running under pytest
  34. '''
  35. def get_salt_run_fixture(self):
  36. pass
  37. def get_salt_run_event_listener(self):
  38. return SaltRunEventListener(None,
  39. self.config,
  40. self.config_dir,
  41. self.bin_dir_path,
  42. self.log_prefix,
  43. cli_script_name='run')
  44. class Salt(ScriptPathMixin, PytestSalt):
  45. '''
  46. Class which runs salt-call commands
  47. '''
  48. def __init__(self, *args, **kwargs):
  49. super(Salt, self).__init__(None, *args, **kwargs)
  50. class SaltCall(ScriptPathMixin, PytestSaltCall):
  51. '''
  52. Class which runs salt-call commands
  53. '''
  54. def __init__(self, *args, **kwargs):
  55. super(SaltCall, self).__init__(None, *args, **kwargs)
  56. class SaltKey(ScriptPathMixin, PytestSaltKey):
  57. '''
  58. Class which runs salt-key commands
  59. '''
  60. def __init__(self, *args, **kwargs):
  61. super(SaltKey, self).__init__(None, *args, **kwargs)
  62. class SaltRun(ScriptPathMixin, PytestSaltRun):
  63. '''
  64. Class which runs salt-run commands
  65. '''
  66. def __init__(self, *args, **kwargs):
  67. super(SaltRun, self).__init__(None, *args, **kwargs)
  68. class SaltProxy(GetSaltRunFixtureMixin, PytestSaltProxy):
  69. '''
  70. Class which runs the salt-proxy daemon
  71. '''
  72. class SaltMinion(GetSaltRunFixtureMixin, PytestSaltMinion):
  73. '''
  74. Class which runs the salt-minion daemon
  75. '''
  76. class SaltMaster(GetSaltRunFixtureMixin, PytestSaltMaster):
  77. '''
  78. Class which runs the salt-master daemon
  79. '''
  80. class SaltSyndic(GetSaltRunFixtureMixin, PytestSaltSyndic):
  81. '''
  82. Class which runs the salt-syndic daemon
  83. '''
  84. def start_daemon(daemon_name=None,
  85. daemon_id=None,
  86. daemon_log_prefix=None,
  87. daemon_cli_script_name=None,
  88. daemon_config=None,
  89. daemon_config_dir=None,
  90. daemon_class=None,
  91. bin_dir_path=None,
  92. fail_hard=False,
  93. start_timeout=10,
  94. slow_stop=False,
  95. environ=None,
  96. cwd=None):
  97. '''
  98. Returns a running salt daemon
  99. '''
  100. daemon_config['pytest_port'] = daemon_config['runtests_conn_check_port']
  101. request = None
  102. if fail_hard:
  103. fail_method = RuntimeError
  104. else:
  105. fail_method = RuntimeWarning
  106. log.info('[%s] Starting pytest %s(%s)', daemon_name, daemon_log_prefix, daemon_id)
  107. attempts = 0
  108. process = None
  109. while attempts <= 3: # pylint: disable=too-many-nested-blocks
  110. attempts += 1
  111. process = daemon_class(request,
  112. daemon_config,
  113. daemon_config_dir,
  114. bin_dir_path,
  115. daemon_log_prefix,
  116. cli_script_name=daemon_cli_script_name,
  117. slow_stop=slow_stop,
  118. environ=environ,
  119. cwd=cwd)
  120. process.start()
  121. if process.is_alive():
  122. try:
  123. connectable = process.wait_until_running(timeout=start_timeout)
  124. if connectable is False:
  125. connectable = process.wait_until_running(timeout=start_timeout/2)
  126. if connectable is False:
  127. process.terminate()
  128. if attempts >= 3:
  129. fail_method(
  130. 'The pytest {0}({1}) has failed to confirm running status '
  131. 'after {2} attempts'.format(daemon_name, daemon_id, attempts))
  132. continue
  133. except Exception as exc: # pylint: disable=broad-except
  134. log.exception('[%s] %s', daemon_log_prefix, exc, exc_info=True)
  135. terminate_process(process.pid, kill_children=True, slow_stop=slow_stop)
  136. if attempts >= 3:
  137. raise fail_method(str(exc))
  138. continue
  139. log.info(
  140. '[%s] The pytest %s(%s) is running and accepting commands '
  141. 'after %d attempts',
  142. daemon_log_prefix,
  143. daemon_name,
  144. daemon_id,
  145. attempts
  146. )
  147. def stop_daemon():
  148. log.info('[%s] Stopping pytest %s(%s)', daemon_log_prefix, daemon_name, daemon_id)
  149. terminate_process(process.pid, kill_children=True, slow_stop=slow_stop)
  150. log.info('[%s] pytest %s(%s) stopped', daemon_log_prefix, daemon_name, daemon_id)
  151. # request.addfinalizer(stop_daemon)
  152. break
  153. else:
  154. terminate_process(process.pid, kill_children=True, slow_stop=slow_stop)
  155. continue
  156. else:
  157. if process is not None:
  158. terminate_process(process.pid, kill_children=True, slow_stop=slow_stop)
  159. raise fail_method(
  160. 'The pytest {0}({1}) has failed to start after {2} attempts'.format(
  161. daemon_name,
  162. daemon_id,
  163. attempts-1
  164. )
  165. )
  166. return process