runtests_engine.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. # -*- coding: utf-8 -*-
  2. '''
  3. :codeauthor: Pedro Algarvio (pedro@algarvio.me)
  4. :copyright: Copyright 2015 by the SaltStack Team, see AUTHORS for more details.
  5. :license: Apache 2.0, see LICENSE for more details.
  6. pytestsalt.engines.pytest_engine
  7. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  8. Simple salt engine which will setup a socket to accept connections allowing us to know
  9. when a daemon is up and running
  10. '''
  11. # Import python libs
  12. from __future__ import absolute_import, print_function, unicode_literals
  13. import os
  14. import sys
  15. import errno
  16. import socket
  17. import logging
  18. # Import salt libs
  19. import salt.utils.event
  20. import salt.utils.asynchronous
  21. # Import 3rd-party libs
  22. from salt.ext.tornado import gen
  23. from salt.ext.tornado import ioloop
  24. from salt.ext.tornado import netutil
  25. from salt.ext.tornado import iostream
  26. log = logging.getLogger(__name__)
  27. __virtualname__ = 'salt_runtests'
  28. def __virtual__():
  29. if __opts__['__role'] != 'master':
  30. return False
  31. return 'runtests_conn_check_port' in __opts__ # pylint: disable=undefined-variable
  32. def start():
  33. pytest_engine = PyTestEngine(__opts__) # pylint: disable=undefined-variable
  34. pytest_engine.start()
  35. class PyTestEngine(object):
  36. def __init__(self, opts):
  37. self.opts = opts
  38. self.sock = None
  39. self.stop_sending_events_file = opts.get('pytest_stop_sending_events_file')
  40. def start(self):
  41. self.io_loop = ioloop.IOLoop()
  42. self.io_loop.make_current()
  43. self.io_loop.add_callback(self._start)
  44. self.io_loop.start()
  45. @gen.coroutine
  46. def _start(self):
  47. port = int(self.opts['runtests_conn_check_port'])
  48. log.info('Starting Pytest Engine(role=%s, id=%s) on port %s', self.opts['__role'], self.opts['id'], port)
  49. self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  50. self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  51. self.sock.setblocking(0)
  52. # bind the socket to localhost on the config provided port
  53. self.sock.bind(('localhost', port))
  54. # become a server socket
  55. self.sock.listen(5)
  56. with salt.utils.asynchronous.current_ioloop(self.io_loop):
  57. netutil.add_accept_handler(
  58. self.sock,
  59. self.handle_connection,
  60. )
  61. if self.opts['__role'] == 'master':
  62. yield self.fire_master_started_event()
  63. def handle_connection(self, connection, address):
  64. log.warning('Accepted connection from %s. Role: %s', address, self.opts['__role'])
  65. # We just need to know that the daemon running the engine is alive...
  66. try:
  67. connection.shutdown(socket.SHUT_RDWR) # pylint: disable=no-member
  68. connection.close()
  69. except socket.error as exc:
  70. if not sys.platform.startswith('darwin'):
  71. raise
  72. try:
  73. if exc.errno != errno.ENOTCONN:
  74. raise
  75. except AttributeError:
  76. # This is not macOS !?
  77. pass
  78. @gen.coroutine
  79. def fire_master_started_event(self):
  80. log.info('Firing salt-%s started event...', self.opts['__role'])
  81. start_event_tag = 'salt/{}/{}/start'.format(self.opts['__role'], self.opts['id'])
  82. log.info('Firing salt-%s started event. Tag: %s', self.opts['__role'], start_event_tag)
  83. load = {'id': self.opts['id'], 'tag': start_event_tag, 'data': {}}
  84. # One minute should be more than enough to fire these events every second in order
  85. # for pytest-salt to pickup that the master is running
  86. with salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=False) as event_bus:
  87. timeout = 30
  88. while True:
  89. if self.stop_sending_events_file and not os.path.exists(self.stop_sending_events_file):
  90. log.info('The stop sending events file "marker" is done. Stop sending events...')
  91. break
  92. timeout -= 1
  93. try:
  94. event_bus.fire_event(load, start_event_tag, timeout=500)
  95. if timeout <= 0:
  96. break
  97. yield gen.sleep(1)
  98. except iostream.StreamClosedError:
  99. break