test_inotify.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. # -*- coding: utf-8 -*-
  2. # Import Python libs
  3. from __future__ import absolute_import, print_function, unicode_literals
  4. import os
  5. import shutil
  6. import tempfile
  7. import time
  8. try:
  9. import pyinotify # pylint: disable=unused-import
  10. HAS_PYINOTIFY = True
  11. except ImportError:
  12. HAS_PYINOTIFY = False
  13. # Import Salt Testing libs
  14. from tests.support.case import MultimasterModuleCase
  15. from tests.support.mixins import AdaptedConfigurationTestCaseMixin
  16. from tests.support.unit import skipIf
  17. # Import salt libs
  18. import salt.version
  19. import salt.config
  20. import logging
  21. log = logging.getLogger(__name__)
  22. @skipIf(not HAS_PYINOTIFY, 'pyinotify is not available')
  23. class TestBeaconsInotify(MultimasterModuleCase, AdaptedConfigurationTestCaseMixin):
  24. '''
  25. Validate the inotify beacon in multimaster environment
  26. '''
  27. def setUp(self):
  28. self.tmpdir = salt.utils.stringutils.to_unicode(tempfile.mkdtemp())
  29. def tearDown(self):
  30. shutil.rmtree(self.tmpdir, ignore_errors=True)
  31. def test_beacons_duplicate_53344(self):
  32. # Also add a status beacon to use it for interval checks
  33. res = self.run_function(
  34. 'beacons.add',
  35. ('inotify', [{'files': {self.tmpdir: {'mask': ['create']}}}]),
  36. master_tgt=0,
  37. )
  38. log.debug('Inotify beacon add returned: %s', res)
  39. self.assertTrue(res.get('result'))
  40. res = self.run_function(
  41. 'beacons.add',
  42. ('status', [{'time': ['all']}]),
  43. master_tgt=0,
  44. )
  45. log.debug('Status beacon add returned: %s', res)
  46. self.assertTrue(res.get('result'))
  47. # Ensure beacons are added.
  48. res = self.run_function(
  49. 'beacons.list',
  50. (),
  51. return_yaml=False,
  52. master_tgt=0,
  53. )
  54. log.debug('Beacons list: %s', res)
  55. self.assertEqual({
  56. 'inotify': [{
  57. 'files': {
  58. self.tmpdir: {
  59. 'mask': ['create']
  60. }
  61. }
  62. }],
  63. 'status': [{
  64. 'time': ['all']
  65. }]
  66. }, res)
  67. file_path = os.path.join(self.tmpdir, 'tmpfile')
  68. try:
  69. master_listener = salt.utils.event.get_event(
  70. 'master',
  71. sock_dir=self.mm_master_opts['sock_dir'],
  72. transport=self.mm_master_opts['transport'],
  73. opts=self.mm_master_opts)
  74. sub_master_listener = salt.utils.event.get_event(
  75. 'master',
  76. sock_dir=self.mm_sub_master_opts['sock_dir'],
  77. transport=self.mm_sub_master_opts['transport'],
  78. opts=self.mm_sub_master_opts)
  79. # We have to wait beacon first execution that would configure the inotify watch.
  80. # Since beacons will be executed both together waiting for the first status beacon event
  81. # which will mean the inotify beacon was executed too.
  82. start = time.time()
  83. stop_at = start + self.mm_minion_opts['loop_interval'] * 2 + 60
  84. event = sub_event = None
  85. while True:
  86. if time.time() > stop_at:
  87. break
  88. if not event:
  89. event = master_listener.get_event(
  90. full=True,
  91. wait=1,
  92. tag='salt/beacon/minion/status',
  93. match_type='startswith',
  94. )
  95. if sub_event is None:
  96. sub_event = sub_master_listener.get_event(
  97. full=True,
  98. wait=1,
  99. tag='salt/beacon/minion/status',
  100. match_type='startswith',
  101. )
  102. if event and sub_event:
  103. break
  104. log.debug('Status events received: %s, %s', event, sub_event)
  105. with salt.utils.files.fopen(file_path, 'w') as f:
  106. pass
  107. start = time.time()
  108. # Now in successful case this test will get results at most in 2 loop intervals.
  109. # Waiting for 2 loops intervals + some seconds to the hardware stupidity.
  110. stop_at = start + self.mm_minion_opts['loop_interval'] * 2 + 60
  111. event = sub_event = None
  112. while True:
  113. if time.time() > stop_at:
  114. break
  115. if not event:
  116. event = master_listener.get_event(
  117. full=True,
  118. wait=1,
  119. tag='salt/beacon/minion/inotify/' + self.tmpdir,
  120. match_type='startswith',
  121. )
  122. if sub_event is None:
  123. sub_event = sub_master_listener.get_event(
  124. full=True,
  125. wait=1,
  126. tag='salt/beacon/minion/inotify/' + self.tmpdir,
  127. match_type='startswith',
  128. )
  129. if event and sub_event:
  130. break
  131. log.debug('Inotify events received: %s, %s', event, sub_event)
  132. finally:
  133. self.assertTrue(self.run_function(
  134. 'beacons.delete',
  135. ('inotify',),
  136. master_tgt=0,
  137. ))
  138. self.assertTrue(self.run_function(
  139. 'beacons.delete',
  140. ('status',),
  141. master_tgt=0,
  142. ))
  143. master_listener.destroy()
  144. sub_master_listener.destroy()
  145. # We can't determine the timestamp so remove it from results
  146. if event:
  147. del event['data']['_stamp']
  148. if sub_event:
  149. del sub_event['data']['_stamp']
  150. expected = {
  151. 'data': {
  152. 'path': file_path,
  153. 'change': 'IN_CREATE',
  154. 'id': 'minion',
  155. },
  156. 'tag': salt.utils.stringutils.to_str('salt/beacon/minion/inotify/' + self.tmpdir),
  157. }
  158. # It's better to compare both at once to see both responses in the error log.
  159. self.assertEqual((expected, expected), (event, sub_event))