test_inotify.py 7.9 KB

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