1
0

test_inotify.py 5.5 KB

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