test_inotify.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. # coding: utf-8
  2. # Python libs
  3. from __future__ import absolute_import
  4. import logging
  5. import os
  6. import shutil
  7. import tempfile
  8. # Salt libs
  9. import salt.utils.files
  10. from salt.beacons import inotify
  11. from tests.support.mixins import LoaderModuleMockMixin
  12. # Salt testing libs
  13. from tests.support.unit import TestCase, skipIf
  14. # Third-party libs
  15. try:
  16. import pyinotify # pylint: disable=unused-import
  17. HAS_PYINOTIFY = True
  18. except ImportError:
  19. HAS_PYINOTIFY = False
  20. log = logging.getLogger(__name__)
  21. @skipIf(not HAS_PYINOTIFY, "pyinotify is not available")
  22. class INotifyBeaconTestCase(TestCase, LoaderModuleMockMixin):
  23. """
  24. Test case for salt.beacons.inotify
  25. """
  26. def setup_loader_modules(self):
  27. return {inotify: {}}
  28. def setUp(self):
  29. self.tmpdir = tempfile.mkdtemp()
  30. def tearDown(self):
  31. shutil.rmtree(self.tmpdir, ignore_errors=True)
  32. def test_non_list_config(self):
  33. config = {}
  34. ret = inotify.validate(config)
  35. self.assertEqual(
  36. ret, (False, "Configuration for inotify beacon must be a list.")
  37. )
  38. def test_empty_config(self):
  39. config = [{}]
  40. ret = inotify.validate(config)
  41. _expected = (False, "Configuration for inotify beacon must include files.")
  42. self.assertEqual(ret, _expected)
  43. def test_files_none_config(self):
  44. config = [{"files": None}]
  45. ret = inotify.validate(config)
  46. _expected = (
  47. False,
  48. "Configuration for inotify beacon invalid, files must be a dict.",
  49. )
  50. self.assertEqual(ret, _expected)
  51. def test_files_list_config(self):
  52. config = [{"files": [{u"/importantfile": {u"mask": [u"modify"]}}]}]
  53. ret = inotify.validate(config)
  54. _expected = (
  55. False,
  56. "Configuration for inotify beacon invalid, files must be a dict.",
  57. )
  58. self.assertEqual(ret, _expected)
  59. def test_file_open(self):
  60. path = os.path.realpath(__file__)
  61. config = [{"files": {path: {"mask": ["open"]}}}]
  62. ret = inotify.validate(config)
  63. self.assertEqual(ret, (True, "Valid beacon configuration"))
  64. ret = inotify.beacon(config)
  65. self.assertEqual(ret, [])
  66. with salt.utils.files.fopen(path, "r") as f:
  67. pass
  68. ret = inotify.beacon(config)
  69. self.assertEqual(len(ret), 1)
  70. self.assertEqual(ret[0]["path"], path)
  71. self.assertEqual(ret[0]["change"], "IN_OPEN")
  72. def test_dir_no_auto_add(self):
  73. config = [{"files": {self.tmpdir: {"mask": ["create"]}}}]
  74. ret = inotify.validate(config)
  75. self.assertEqual(ret, (True, "Valid beacon configuration"))
  76. ret = inotify.beacon(config)
  77. self.assertEqual(ret, [])
  78. fp = os.path.join(self.tmpdir, "tmpfile")
  79. with salt.utils.files.fopen(fp, "w") as f:
  80. pass
  81. ret = inotify.beacon(config)
  82. self.assertEqual(len(ret), 1)
  83. self.assertEqual(ret[0]["path"], fp)
  84. self.assertEqual(ret[0]["change"], "IN_CREATE")
  85. with salt.utils.files.fopen(fp, "r") as f:
  86. pass
  87. ret = inotify.beacon(config)
  88. self.assertEqual(ret, [])
  89. def test_dir_auto_add(self):
  90. config = [
  91. {"files": {self.tmpdir: {"mask": ["create", "open"], "auto_add": True}}}
  92. ]
  93. ret = inotify.validate(config)
  94. self.assertEqual(ret, (True, "Valid beacon configuration"))
  95. ret = inotify.beacon(config)
  96. self.assertEqual(ret, [])
  97. fp = os.path.join(self.tmpdir, "tmpfile")
  98. with salt.utils.files.fopen(fp, "w") as f:
  99. pass
  100. ret = inotify.beacon(config)
  101. self.assertEqual(len(ret), 2)
  102. self.assertEqual(ret[0]["path"], fp)
  103. self.assertEqual(ret[0]["change"], "IN_CREATE")
  104. self.assertEqual(ret[1]["path"], fp)
  105. self.assertEqual(ret[1]["change"], "IN_OPEN")
  106. with salt.utils.files.fopen(fp, "r") as f:
  107. pass
  108. ret = inotify.beacon(config)
  109. self.assertEqual(len(ret), 1)
  110. self.assertEqual(ret[0]["path"], fp)
  111. self.assertEqual(ret[0]["change"], "IN_OPEN")
  112. def test_dir_recurse(self):
  113. dp1 = os.path.join(self.tmpdir, "subdir1")
  114. os.mkdir(dp1)
  115. dp2 = os.path.join(dp1, "subdir2")
  116. os.mkdir(dp2)
  117. fp = os.path.join(dp2, "tmpfile")
  118. with salt.utils.files.fopen(fp, "w") as f:
  119. pass
  120. config = [{"files": {self.tmpdir: {"mask": ["open"], "recurse": True}}}]
  121. ret = inotify.validate(config)
  122. self.assertEqual(ret, (True, "Valid beacon configuration"))
  123. ret = inotify.beacon(config)
  124. self.assertEqual(ret, [])
  125. with salt.utils.files.fopen(fp) as f:
  126. pass
  127. ret = inotify.beacon(config)
  128. self.assertEqual(len(ret), 3)
  129. self.assertEqual(ret[0]["path"], dp1)
  130. self.assertEqual(ret[0]["change"], "IN_OPEN|IN_ISDIR")
  131. self.assertEqual(ret[1]["path"], dp2)
  132. self.assertEqual(ret[1]["change"], "IN_OPEN|IN_ISDIR")
  133. self.assertEqual(ret[2]["path"], fp)
  134. self.assertEqual(ret[2]["change"], "IN_OPEN")
  135. def test_dir_recurse_auto_add(self):
  136. dp1 = os.path.join(self.tmpdir, "subdir1")
  137. os.mkdir(dp1)
  138. config = [
  139. {
  140. "files": {
  141. self.tmpdir: {
  142. "mask": ["create", "delete"],
  143. "recurse": True,
  144. "auto_add": True,
  145. }
  146. }
  147. }
  148. ]
  149. ret = inotify.validate(config)
  150. self.assertEqual(ret, (True, "Valid beacon configuration"))
  151. ret = inotify.beacon(config)
  152. self.assertEqual(ret, [])
  153. dp2 = os.path.join(dp1, "subdir2")
  154. os.mkdir(dp2)
  155. ret = inotify.beacon(config)
  156. self.assertEqual(len(ret), 1)
  157. self.assertEqual(ret[0]["path"], dp2)
  158. self.assertEqual(ret[0]["change"], "IN_CREATE|IN_ISDIR")
  159. fp = os.path.join(dp2, "tmpfile")
  160. with salt.utils.files.fopen(fp, "w") as f:
  161. pass
  162. ret = inotify.beacon(config)
  163. self.assertEqual(len(ret), 1)
  164. self.assertEqual(ret[0]["path"], fp)
  165. self.assertEqual(ret[0]["change"], "IN_CREATE")
  166. os.remove(fp)
  167. ret = inotify.beacon(config)
  168. self.assertEqual(len(ret), 1)
  169. self.assertEqual(ret[0]["path"], fp)
  170. self.assertEqual(ret[0]["change"], "IN_DELETE")
  171. def test_multi_files_exclude(self):
  172. dp1 = os.path.join(self.tmpdir, "subdir1")
  173. dp2 = os.path.join(self.tmpdir, "subdir2")
  174. os.mkdir(dp1)
  175. os.mkdir(dp2)
  176. _exclude1 = "{0}/subdir1/*tmpfile*$".format(self.tmpdir)
  177. _exclude2 = "{0}/subdir2/*filetmp*$".format(self.tmpdir)
  178. config = [
  179. {
  180. "files": {
  181. dp1: {
  182. "mask": ["create", "delete"],
  183. "recurse": True,
  184. "exclude": [{_exclude1: {"regex": True}}],
  185. "auto_add": True,
  186. }
  187. }
  188. },
  189. {
  190. "files": {
  191. dp2: {
  192. "mask": ["create", "delete"],
  193. "recurse": True,
  194. "exclude": [{_exclude2: {"regex": True}}],
  195. "auto_add": True,
  196. }
  197. }
  198. },
  199. ]
  200. ret = inotify.validate(config)
  201. self.assertEqual(ret, (True, "Valid beacon configuration"))
  202. fp = os.path.join(dp1, "tmpfile")
  203. with salt.utils.files.fopen(fp, "w") as f:
  204. pass
  205. ret = inotify.beacon(config)
  206. self.assertEqual(len(ret), 0)
  207. os.remove(fp)
  208. ret = inotify.beacon(config)
  209. self.assertEqual(len(ret), 0)
  210. fp = os.path.join(dp2, "tmpfile")
  211. with salt.utils.files.fopen(fp, "w") as f:
  212. pass
  213. ret = inotify.beacon(config)
  214. self.assertEqual(len(ret), 1)
  215. self.assertEqual(ret[0]["path"], fp)
  216. self.assertEqual(ret[0]["change"], "IN_CREATE")
  217. os.remove(fp)
  218. ret = inotify.beacon(config)
  219. self.assertEqual(len(ret), 1)
  220. self.assertEqual(ret[0]["path"], fp)
  221. self.assertEqual(ret[0]["change"], "IN_DELETE")