TestHostCleanup.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. #!/usr/bin/env python2.6
  2. '''
  3. Licensed to the Apache Software Foundation (ASF) under one
  4. or more contributor license agreements. See the NOTICE file
  5. distributed with this work for additional information
  6. regarding copyright ownership. The ASF licenses this file
  7. to you under the Apache License, Version 2.0 (the
  8. "License"); you may not use this file except in compliance
  9. with the License. You may obtain a copy of the License at
  10. http://www.apache.org/licenses/LICENSE-2.0
  11. Unless required by applicable law or agreed to in writing, software
  12. distributed under the License is distributed on an "AS IS" BASIS,
  13. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. See the License for the specific language governing permissions and
  15. limitations under the License.
  16. '''
  17. from unittest import TestCase
  18. import unittest
  19. from mock.mock import patch, MagicMock, call, create_autospec
  20. from ambari_agent import HostCleanup
  21. import StringIO
  22. import sys
  23. import tempfile
  24. import os.path
  25. import optparse
  26. import logging
  27. PACKAGE_SECTION = "packages"
  28. PACKAGE_KEY = "pkg_list"
  29. USER_SECTION = "users"
  30. USER_KEY = "usr_list"
  31. REPO_SECTION = "repositories"
  32. REPOS_KEY = "pkg_list"
  33. DIR_SECTION = "directories"
  34. DIR_KEY = "dir_list"
  35. PROCESS_SECTION = "processes"
  36. PROCESS_KEY = "proc_list"
  37. ALT_SECTION = "alternatives"
  38. ALT_KEYS = ["symlink_list", "target_list"]
  39. ALT_ERASE_CMD = "alternatives --remove {0} {1}"
  40. USER_HOMEDIR_SECTION = "usr_homedir"
  41. class TestHostCleanup(TestCase):
  42. def setUp(self):
  43. HostCleanup.logger = MagicMock()
  44. self.hostcleanup = HostCleanup.HostCleanup()
  45. # disable stdout
  46. out = StringIO.StringIO()
  47. sys.stdout = out
  48. def tearDown(self):
  49. # enable stdout
  50. sys.stdout = sys.__stdout__
  51. def test_read_host_check_file_with_content(self):
  52. out = StringIO.StringIO()
  53. sys.stdout = out
  54. tmpfile = tempfile.mktemp()
  55. f = open(tmpfile,'w')
  56. fileContent = """[processes]
  57. proc_list = 323,434
  58. [users]
  59. usr_list = rrdcached,ambari-qa,hive,oozie,hbase,hcat,mysql,mapred,hdfs,zookeeper,sqoop,nagios
  60. [repositories]
  61. repo_list = HDP-1.3.0,HDP-epel
  62. [directories]
  63. dir_list = /etc/hadoop,/etc/hbase,/etc/hcatalog,/tmp/hive,/tmp/nagios,/var/nagios
  64. [alternatives]
  65. symlink_list = hcatalog-conf,hadoop-default,hadoop-log,oozie-conf
  66. target_list = /etc/hcatalog/conf.dist,/usr/share/man/man1/hadoop.1.gz,/etc/oozie/conf.dist,/usr/lib/hadoop
  67. [packages]
  68. pkg_list = sqoop.noarch,hadoop-libhdfs.x86_64,rrdtool.x86_64,ganglia-gmond.x86_64
  69. [metadata]
  70. created = 2013-07-02 20:39:22.162757"""
  71. f.write(fileContent)
  72. f.close()
  73. propMap = self.hostcleanup.read_host_check_file(tmpfile)
  74. self.assertTrue("323" in propMap["processes"])
  75. self.assertTrue("mysql" in propMap["users"])
  76. self.assertTrue("HDP-epel" in propMap["repositories"])
  77. self.assertTrue("/etc/hadoop" in propMap["directories"])
  78. self.assertTrue("hcatalog-conf" in propMap["alternatives"]["symlink_list"])
  79. self.assertTrue("/etc/oozie/conf.dist" in propMap["alternatives"]["target_list"])
  80. self.assertTrue("hadoop-libhdfs.x86_64" in propMap["packages"])
  81. sys.stdout = sys.__stdout__
  82. class HostCleanupOptions:
  83. def __init__(self, outputfile, inputfile, skip, verbose):
  84. self.outputfile = outputfile
  85. self.inputfile = inputfile
  86. self.skip = skip
  87. self.verbose = False
  88. @patch.object(HostCleanup.HostCleanup, 'do_cleanup')
  89. @patch.object(HostCleanup.HostCleanup, 'is_current_user_root')
  90. @patch.object(logging.FileHandler, 'setFormatter')
  91. @patch.object(HostCleanup.HostCleanup,'read_host_check_file')
  92. @patch.object(logging,'basicConfig')
  93. @patch.object(logging, 'FileHandler')
  94. @patch.object(optparse.OptionParser, 'parse_args')
  95. def test_options(self, parser_mock, file_handler_mock, logging_mock, read_host_check_file_mock, set_formatter_mock, user_root_mock, do_cleanup_mock):
  96. parser_mock.return_value = (TestHostCleanup.HostCleanupOptions('/someoutputfile', '/someinputfile', '', False), [])
  97. file_handler_mock.return_value = logging.FileHandler('') # disable creating real file
  98. user_root_mock.return_value = True
  99. HostCleanup.main()
  100. # test --out
  101. file_handler_mock.assert_called_with('/someoutputfile')
  102. # test --skip
  103. self.assertEquals([''],HostCleanup.SKIP_LIST)
  104. #test --verbose
  105. logging_mock.assert_called_with(level=logging.INFO)
  106. # test --in
  107. read_host_check_file_mock.assert_called_with('/someinputfile')
  108. @patch.object(HostCleanup.HostCleanup, 'do_erase_alternatives')
  109. @patch.object(HostCleanup.HostCleanup, 'find_repo_files_for_repos')
  110. @patch.object(HostCleanup.HostCleanup, 'get_os_type')
  111. @patch.object(HostCleanup.HostCleanup, 'do_kill_processes')
  112. @patch.object(HostCleanup.HostCleanup, 'do_erase_files_silent')
  113. @patch.object(HostCleanup.HostCleanup, 'do_erase_dir_silent')
  114. @patch.object(HostCleanup.HostCleanup, 'do_delete_users')
  115. @patch.object(HostCleanup.HostCleanup, 'do_erase_packages')
  116. def test_do_cleanup_all(self, do_erase_packages_method, do_delete_users_method,
  117. do_erase_dir_silent_method,
  118. do_erase_files_silent_method, do_kill_processes_method,
  119. get_os_type_method, find_repo_files_for_repos_method,
  120. do_erase_alternatives_method):
  121. global SKIP_LIST
  122. oldSkipList = HostCleanup.SKIP_LIST
  123. HostCleanup.SKIP_LIST = []
  124. out = StringIO.StringIO()
  125. sys.stdout = out
  126. propertyMap = {PACKAGE_SECTION:['abcd', 'pqrst'], USER_SECTION:['abcd', 'pqrst'],
  127. REPO_SECTION:['abcd', 'pqrst'], DIR_SECTION:['abcd', 'pqrst'],
  128. PROCESS_SECTION:['abcd', 'pqrst'],
  129. ALT_SECTION:{ALT_KEYS[0]:['alt1','alt2'], ALT_KEYS[1]:[
  130. 'dir1']}, USER_HOMEDIR_SECTION:['decf']}
  131. get_os_type_method.return_value = 'redhat'
  132. find_repo_files_for_repos_method.return_value = ['abcd', 'pqrst']
  133. self.hostcleanup.do_cleanup(propertyMap)
  134. self.assertTrue(do_delete_users_method.called)
  135. self.assertTrue(do_erase_dir_silent_method.called)
  136. self.assertTrue(do_erase_files_silent_method.called)
  137. self.assertTrue(do_erase_packages_method.called)
  138. self.assertTrue(do_kill_processes_method.called)
  139. self.assertTrue(do_erase_alternatives_method.called)
  140. calls = [call(['decf']), call(['abcd', 'pqrst'])]
  141. do_erase_dir_silent_method.assert_has_calls(calls)
  142. do_erase_packages_method.assert_called_once_with(['abcd', 'pqrst'])
  143. do_erase_files_silent_method.assert_called_once_with(['abcd', 'pqrst'])
  144. do_delete_users_method.assert_called_once_with(['abcd', 'pqrst'])
  145. do_kill_processes_method.assert_called_once_with(['abcd', 'pqrst'])
  146. do_erase_alternatives_method.assert_called_once_with({ALT_KEYS[0]:['alt1',
  147. 'alt2'], ALT_KEYS[1]:['dir1']})
  148. HostCleanup.SKIP_LIST = oldSkipList
  149. sys.stdout = sys.__stdout__
  150. @patch.object(HostCleanup.HostCleanup, 'do_delete_by_owner')
  151. @patch.object(HostCleanup.HostCleanup, 'get_user_ids')
  152. @patch.object(HostCleanup.HostCleanup, 'do_erase_alternatives')
  153. @patch.object(HostCleanup.HostCleanup, 'find_repo_files_for_repos')
  154. @patch.object(HostCleanup.HostCleanup, 'get_os_type')
  155. @patch.object(HostCleanup.HostCleanup, 'do_kill_processes')
  156. @patch.object(HostCleanup.HostCleanup, 'do_erase_files_silent')
  157. @patch.object(HostCleanup.HostCleanup, 'do_erase_dir_silent')
  158. @patch.object(HostCleanup.HostCleanup, 'do_delete_users')
  159. @patch.object(HostCleanup.HostCleanup, 'do_erase_packages')
  160. def test_do_cleanup_default(self, do_erase_packages_method, do_delete_users_method,
  161. do_erase_dir_silent_method,
  162. do_erase_files_silent_method, do_kill_processes_method,
  163. get_os_type_method, find_repo_files_for_repos_method,
  164. do_erase_alternatives_method, get_user_ids_method,
  165. do_delete_by_owner_method):
  166. out = StringIO.StringIO()
  167. sys.stdout = out
  168. propertyMap = {PACKAGE_SECTION:['abcd', 'pqrst'], USER_SECTION:['abcd', 'pqrst'],
  169. REPO_SECTION:['abcd', 'pqrst'], DIR_SECTION:['abcd', 'pqrst'],
  170. PROCESS_SECTION:['abcd', 'pqrst'],
  171. ALT_SECTION:{ALT_KEYS[0]:['alt1','alt2'], ALT_KEYS[1]:[
  172. 'dir1']}}
  173. get_os_type_method.return_value = 'redhat'
  174. find_repo_files_for_repos_method.return_value = ['abcd', 'pqrst']
  175. self.hostcleanup.do_cleanup(propertyMap)
  176. self.assertFalse(do_delete_by_owner_method.called)
  177. self.assertFalse(get_user_ids_method.called)
  178. self.assertFalse(do_delete_users_method.called)
  179. self.assertTrue(do_erase_dir_silent_method.called)
  180. self.assertTrue(do_erase_files_silent_method.called)
  181. self.assertTrue(do_erase_packages_method.called)
  182. self.assertTrue(do_kill_processes_method.called)
  183. self.assertTrue(do_erase_alternatives_method.called)
  184. sys.stdout = sys.__stdout__
  185. @patch.object(HostCleanup.HostCleanup, 'find_repo_files_for_repos')
  186. @patch.object(HostCleanup.HostCleanup, 'get_os_type')
  187. @patch.object(HostCleanup.HostCleanup, 'do_kill_processes')
  188. @patch.object(HostCleanup.HostCleanup, 'do_erase_files_silent')
  189. @patch.object(HostCleanup.HostCleanup, 'do_erase_dir_silent')
  190. @patch.object(HostCleanup.HostCleanup, 'do_delete_users')
  191. @patch.object(HostCleanup.HostCleanup, 'do_erase_packages')
  192. def test_do_cleanup_with_skip(self, do_erase_packages_method,
  193. do_delete_users_method,
  194. do_erase_dir_silent_method,
  195. do_erase_files_silent_method, do_kill_processes_method,
  196. get_os_type_method, find_repo_files_for_repos_method):
  197. out = StringIO.StringIO()
  198. sys.stdout = out
  199. propertyMap = {PACKAGE_SECTION:['abcd', 'pqrst'], USER_SECTION:['abcd', 'pqrst'],
  200. REPO_SECTION:['abcd', 'pqrst'], DIR_SECTION:['abcd', 'pqrst'],
  201. PROCESS_SECTION:['abcd', 'pqrst']}
  202. get_os_type_method.return_value = 'redhat'
  203. find_repo_files_for_repos_method.return_value = ['abcd', 'pqrst']
  204. HostCleanup.SKIP_LIST = [PACKAGE_SECTION, REPO_SECTION]
  205. self.hostcleanup.do_cleanup(propertyMap)
  206. self.assertTrue(do_delete_users_method.called)
  207. self.assertTrue(do_erase_dir_silent_method.called)
  208. self.assertFalse(do_erase_files_silent_method.called)
  209. self.assertFalse(do_erase_packages_method.called)
  210. self.assertTrue(do_kill_processes_method.called)
  211. calls = [call(None), call(['abcd', 'pqrst'])]
  212. do_erase_dir_silent_method.assert_has_calls(calls)
  213. do_delete_users_method.assert_called_once_with(['abcd', 'pqrst'])
  214. do_kill_processes_method.assert_called_once_with(['abcd', 'pqrst'])
  215. sys.stdout = sys.__stdout__
  216. @patch.object(HostCleanup.HostCleanup, 'do_erase_dir_silent')
  217. @patch("os.stat")
  218. @patch("os.path.join")
  219. @patch("os.listdir")
  220. def test_do_delete_by_owner(self, listdir_mock, join_mock, stat_mock, do_erase_dir_silent_method):
  221. listdir_mock.return_value = ["k", "j"]
  222. join_mock.return_value = "path"
  223. response = MagicMock()
  224. response.st_uid = 1
  225. stat_mock.return_value = response
  226. self.hostcleanup.do_delete_by_owner([1, 2], ["a"])
  227. self.assertTrue(do_erase_dir_silent_method.called)
  228. calls = [call(["path"]), call(["path"])]
  229. do_erase_dir_silent_method.assert_has_calls(calls)
  230. @patch.object(HostCleanup.HostCleanup, 'run_os_command')
  231. def test_do_delete_users(self, run_os_command_mock):
  232. run_os_command_mock.return_value = (1, "", "")
  233. self.hostcleanup.do_delete_users(["a", "b"])
  234. self.assertTrue(run_os_command_mock.called)
  235. calls = [call('userdel -rf a'), call('userdel -rf b'), call('groupdel hadoop')]
  236. run_os_command_mock.assert_has_calls(calls)
  237. @patch("ConfigParser.RawConfigParser")
  238. @patch("__builtin__.open")
  239. def test_read_host_check_file(self, openMock, readMock):
  240. out = StringIO.StringIO()
  241. sys.stdout = out
  242. f = MagicMock()
  243. openMock.return_value = f
  244. propertyMap = self.hostcleanup.read_host_check_file('test')
  245. self.assertTrue(openMock.called)
  246. self.assertTrue(readMock.called)
  247. self.assertTrue(propertyMap.has_key(PACKAGE_SECTION))
  248. self.assertTrue(propertyMap.has_key(REPO_SECTION))
  249. self.assertTrue(propertyMap.has_key(USER_SECTION))
  250. self.assertTrue(propertyMap.has_key(DIR_SECTION))
  251. self.assertTrue(propertyMap.has_key(PROCESS_SECTION))
  252. sys.stdout = sys.__stdout__
  253. @patch.object(HostCleanup.HostCleanup, 'run_os_command')
  254. @patch.object(HostCleanup.HostCleanup, 'get_os_type')
  255. def test_do_earse_packages(self, get_os_type_method, run_os_command_method):
  256. out = StringIO.StringIO()
  257. sys.stdout = out
  258. get_os_type_method.return_value = 'redhat'
  259. run_os_command_method.return_value = (0, 'success', 'success')
  260. retval = self.hostcleanup.do_erase_packages(['abcd', 'wxyz'])
  261. self.assertTrue(get_os_type_method.called)
  262. self.assertTrue(run_os_command_method.called)
  263. run_os_command_method.assert_called_with("yum erase -y {0}".format(' '
  264. .join(['abcd', 'wxyz'])))
  265. self.assertEquals(0, retval)
  266. get_os_type_method.reset()
  267. run_os_command_method.reset()
  268. get_os_type_method.return_value = 'suse'
  269. run_os_command_method.return_value = (0, 'success', 'success')
  270. retval = self.hostcleanup.do_erase_packages(['abcd', 'wxyz'])
  271. self.assertTrue(get_os_type_method.called)
  272. self.assertTrue(run_os_command_method.called)
  273. run_os_command_method.assert_called_with("zypper -n -q remove {0}"
  274. .format(' '.join(['abcd', 'wxyz'])))
  275. self.assertEquals(0, retval)
  276. sys.stdout = sys.__stdout__
  277. @patch.object(HostCleanup.HostCleanup, 'get_files_in_dir')
  278. @patch.object(HostCleanup.HostCleanup, 'get_os_type')
  279. def test_find_repo_files_for_repos(self, get_os_type_method,
  280. get_files_in_dir_method):
  281. out = StringIO.StringIO()
  282. sys.stdout = out
  283. tmpfile = tempfile.mktemp()
  284. fileContent = """[###]
  285. [aass]
  286. [$$]
  287. 444]saas[333
  288. 1122[naas]2222
  289. name=sd des derft 3.1
  290. """
  291. with open(tmpfile,'w') as file:
  292. file.write(fileContent)
  293. get_os_type_method.return_value = 'redhat'
  294. get_files_in_dir_method.return_value = [ tmpfile ]
  295. repoFiles = self.hostcleanup.find_repo_files_for_repos(['aass'])
  296. self.assertTrue(get_files_in_dir_method.called)
  297. self.assertTrue(get_os_type_method.called)
  298. self.assertEquals(repoFiles, [ tmpfile ])
  299. repoFiles = self.hostcleanup.find_repo_files_for_repos(['sd des derft 3.1'])
  300. self.assertTrue(get_files_in_dir_method.called)
  301. self.assertTrue(get_os_type_method.called)
  302. self.assertEquals(repoFiles, [ tmpfile ])
  303. repoFiles = self.hostcleanup.find_repo_files_for_repos(['sd des derft 3.1', 'aass'])
  304. self.assertEquals(repoFiles, [ tmpfile ])
  305. repoFiles = self.hostcleanup.find_repo_files_for_repos(['saas'])
  306. self.assertEquals(repoFiles, [])
  307. repoFiles = self.hostcleanup.find_repo_files_for_repos([''])
  308. self.assertEquals(repoFiles, [])
  309. sys.stdout = sys.__stdout__
  310. @patch.object(HostCleanup.HostCleanup, 'run_os_command')
  311. @patch.object(HostCleanup.HostCleanup, 'do_erase_dir_silent')
  312. @patch.object(HostCleanup.HostCleanup, 'get_alternatives_desc')
  313. def test_do_erase_alternatives(self, get_alternatives_desc_mock,
  314. do_erase_dir_silent_mock, run_os_command_mock):
  315. out = StringIO.StringIO()
  316. sys.stdout = out
  317. get_alternatives_desc_mock.return_value = 'somepath to alternative\n'
  318. run_os_command_mock.return_value = (0, None, None)
  319. alt_map = {ALT_KEYS[0]:['alt1'], ALT_KEYS[1]:['dir1']}
  320. self.hostcleanup.do_erase_alternatives(alt_map)
  321. self.assertTrue(get_alternatives_desc_mock.called)
  322. get_alternatives_desc_mock.called_once_with('alt1')
  323. self.assertTrue(run_os_command_mock.called)
  324. run_os_command_mock.called_once_with(ALT_ERASE_CMD.format('alt1', 'somepath'))
  325. self.assertTrue(do_erase_dir_silent_mock.called)
  326. do_erase_dir_silent_mock.called_once_with(['dir1'])
  327. sys.stdout = sys.__stdout__
  328. if __name__ == "__main__":
  329. unittest.main(verbosity=2)