1
0

runtests_engine.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  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 sys
  14. import errno
  15. import socket
  16. import logging
  17. # Import salt libs
  18. import salt.utils.event
  19. import salt.utils.asynchronous
  20. # Import 3rd-party libs
  21. from tornado import gen
  22. from tornado import ioloop
  23. from tornado import netutil
  24. from tornado import iostream
  25. log = logging.getLogger(__name__)
  26. __virtualname__ = 'salt_runtests'
  27. def __virtual__():
  28. if __opts__['__role'] != 'master':
  29. return False
  30. return 'runtests_conn_check_port' in __opts__ # pylint: disable=undefined-variable
  31. def start():
  32. pytest_engine = PyTestEngine(__opts__) # pylint: disable=undefined-variable
  33. pytest_engine.start()
  34. class PyTestEngine(object):
  35. def __init__(self, opts):
  36. self.opts = opts
  37. self.sock = None
  38. def start(self):
  39. self.io_loop = ioloop.IOLoop()
  40. self.io_loop.make_current()
  41. self.io_loop.add_callback(self._start)
  42. self.io_loop.start()
  43. @gen.coroutine
  44. def _start(self):
  45. self.io_loop.spawn_callback(self.fire_master_started_event)
  46. port = int(self.opts['runtests_conn_check_port'])
  47. log.info('Starting Pytest Engine(role=%s) on port %s', self.opts['__role'], port)
  48. self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  49. self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  50. self.sock.setblocking(0)
  51. # bind the socket to localhost on the config provided port
  52. self.sock.bind(('localhost', port))
  53. # become a server socket
  54. self.sock.listen(5)
  55. with salt.utils.asynchronous.current_ioloop(self.io_loop):
  56. netutil.add_accept_handler(
  57. self.sock,
  58. self.handle_connection,
  59. )
  60. def handle_connection(self, connection, address):
  61. log.warning('Accepted connection from %s. Role: %s', address, self.opts['__role'])
  62. # We just need to know that the daemon running the engine is alive...
  63. try:
  64. connection.shutdown(socket.SHUT_RDWR) # pylint: disable=no-member
  65. connection.close()
  66. except socket.error as exc:
  67. if not sys.platform.startswith('darwin'):
  68. raise
  69. try:
  70. if exc.errno != errno.ENOTCONN:
  71. raise
  72. except AttributeError:
  73. # This is not macOS !?
  74. pass
  75. @gen.coroutine
  76. def fire_master_started_event(self):
  77. log.info('Firing salt-master started event...')
  78. event_bus = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=False)
  79. master_start_event_tag = 'salt/master/{0}/start'.format(self.opts['id'])
  80. load = {'id': self.opts['id'], 'tag': master_start_event_tag, 'data': {}}
  81. # One minute should be more than enough to fire these events every second in order
  82. # for pytest-salt to pickup that the master is running
  83. timeout = 60
  84. while True:
  85. timeout -= 1
  86. try:
  87. event_bus.fire_event(load, master_start_event_tag, timeout=500)
  88. if timeout <= 0:
  89. break
  90. yield gen.sleep(1)
  91. except iostream.StreamClosedError:
  92. break