TestHostCleanup.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. #!/usr/bin/env python
  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, Mock, 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. @patch("os.listdir", create=True, autospec=True)
  52. def test_read_host_check_file_with_content(self, os_listdir_mock):
  53. out = StringIO.StringIO()
  54. sys.stdout = out
  55. tmpfile = tempfile.mktemp()
  56. f = open(tmpfile,'w')
  57. fileContent = """[processes]
  58. proc_list = 323,434
  59. [users]
  60. usr_list = rrdcached,ambari-qa,hive,oozie,hbase,hcat,mysql,mapred,hdfs,zookeeper,sqoop,nagios
  61. [repositories]
  62. repo_list = HDP-1.3.0,HDP-epel
  63. [directories]
  64. dir_list = /etc/hadoop,/etc/hbase,/etc/hcatalog,/tmp/hive,/tmp/nagios,/var/nagios
  65. [alternatives]
  66. symlink_list = hcatalog-conf,hadoop-default,hadoop-log,oozie-conf
  67. target_list = /etc/hcatalog/conf.dist,/usr/share/man/man1/hadoop.1.gz,/etc/oozie/conf.dist,/usr/lib/hadoop
  68. [packages]
  69. pkg_list = sqoop.noarch,hadoop-libhdfs.x86_64,rrdtool.x86_64,ganglia-gmond.x86_64
  70. [metadata]
  71. created = 2013-07-02 20:39:22.162757"""
  72. f.write(fileContent)
  73. f.close()
  74. os_listdir_mock.return_value = ['111']
  75. tf2 = tempfile.mktemp()
  76. f2 = open(tf2,'w')
  77. f2.write('java_home|hadoop')
  78. f2.close()
  79. with patch('os.path.join') as patch_join_mock:
  80. patch_join_mock.return_value = f2.name
  81. propMap = self.hostcleanup.read_host_check_file(tmpfile)
  82. self.assertTrue(111 in propMap["processes"])
  83. self.assertTrue("mysql" in propMap["users"])
  84. self.assertTrue("HDP-epel" in propMap["repositories"])
  85. self.assertTrue("/etc/hadoop" in propMap["directories"])
  86. self.assertTrue("hcatalog-conf" in propMap["alternatives"]["symlink_list"])
  87. self.assertTrue("/etc/oozie/conf.dist" in propMap["alternatives"]["target_list"])
  88. self.assertTrue("hadoop-libhdfs.x86_64" in propMap["packages"])
  89. sys.stdout = sys.__stdout__
  90. class HostCleanupOptions:
  91. def __init__(self, outputfile, inputfile, skip, verbose, silent, java_home):
  92. self.outputfile = outputfile
  93. self.inputfile = inputfile
  94. self.skip = skip
  95. self.verbose = verbose
  96. self.silent = silent
  97. self.java_home = java_home
  98. @patch.object(HostCleanup, 'get_YN_input')
  99. @patch.object(HostCleanup.HostCleanup, 'do_cleanup')
  100. @patch.object(HostCleanup.HostCleanup, 'is_current_user_root')
  101. @patch.object(logging.FileHandler, 'setFormatter')
  102. @patch.object(HostCleanup.HostCleanup,'read_host_check_file')
  103. @patch.object(logging,'basicConfig')
  104. @patch.object(logging, 'FileHandler')
  105. @patch.object(optparse.OptionParser, 'parse_args')
  106. def test_options(self, parser_mock, file_handler_mock, logging_mock, read_host_check_file_mock,
  107. set_formatter_mock, user_root_mock, do_cleanup_mock, get_yn_input_mock):
  108. parser_mock.return_value = (TestHostCleanup.HostCleanupOptions('/someoutputfile', '/someinputfile', '', False,
  109. False, 'java_home'), [])
  110. file_handler_mock.return_value = logging.FileHandler('') # disable creating real file
  111. user_root_mock.return_value = True
  112. get_yn_input_mock.return_value = True
  113. HostCleanup.main()
  114. # test --out
  115. file_handler_mock.assert_called_with('/someoutputfile')
  116. # test --skip
  117. self.assertEquals([''],HostCleanup.SKIP_LIST)
  118. #test --verbose
  119. logging_mock.assert_called_with(level=logging.INFO)
  120. # test --in
  121. read_host_check_file_mock.assert_called_with('/someinputfile')
  122. self.assertTrue(get_yn_input_mock.called)
  123. @patch.object(HostCleanup, 'get_YN_input')
  124. @patch.object(HostCleanup.HostCleanup, 'do_cleanup')
  125. @patch.object(HostCleanup.HostCleanup, 'is_current_user_root')
  126. @patch.object(logging.FileHandler, 'setFormatter')
  127. @patch.object(HostCleanup.HostCleanup,'read_host_check_file')
  128. @patch.object(logging,'basicConfig')
  129. @patch.object(logging, 'FileHandler')
  130. @patch.object(optparse.OptionParser, 'parse_args')
  131. def test_options_silent(self, parser_mock, file_handler_mock, logging_mock, read_host_check_file_mock,
  132. set_formatter_mock, user_root_mock, do_cleanup_mock, get_yn_input_mock):
  133. parser_mock.return_value = (TestHostCleanup.HostCleanupOptions('/someoutputfile', '/someinputfile', '', False,
  134. True, 'java_home'), [])
  135. file_handler_mock.return_value = logging.FileHandler('') # disable creating real file
  136. user_root_mock.return_value = True
  137. get_yn_input_mock.return_value = True
  138. HostCleanup.main()
  139. # test --out
  140. file_handler_mock.assert_called_with('/someoutputfile')
  141. # test --skip
  142. self.assertEquals([''],HostCleanup.SKIP_LIST)
  143. #test --verbose
  144. logging_mock.assert_called_with(level=logging.INFO)
  145. # test --in
  146. read_host_check_file_mock.assert_called_with('/someinputfile')
  147. self.assertFalse(get_yn_input_mock.called)
  148. @patch.object(HostCleanup.HostCleanup, 'do_erase_alternatives')
  149. @patch.object(HostCleanup.HostCleanup, 'find_repo_files_for_repos')
  150. @patch.object(HostCleanup.HostCleanup, 'get_os_type')
  151. @patch.object(HostCleanup.HostCleanup, 'do_kill_processes')
  152. @patch.object(HostCleanup.HostCleanup, 'do_erase_files_silent')
  153. @patch.object(HostCleanup.HostCleanup, 'do_erase_dir_silent')
  154. @patch.object(HostCleanup.HostCleanup, 'do_delete_users')
  155. @patch.object(HostCleanup.HostCleanup, 'do_erase_packages')
  156. def test_do_cleanup_all(self, do_erase_packages_method, do_delete_users_method,
  157. do_erase_dir_silent_method,
  158. do_erase_files_silent_method, do_kill_processes_method,
  159. get_os_type_method, find_repo_files_for_repos_method,
  160. do_erase_alternatives_method):
  161. out = StringIO.StringIO()
  162. sys.stdout = out
  163. propertyMap = {PACKAGE_SECTION:['abcd', 'pqrst'], USER_SECTION:['abcd', 'pqrst'],
  164. REPO_SECTION:['abcd', 'pqrst'], DIR_SECTION:['abcd', 'pqrst'],
  165. PROCESS_SECTION:['abcd', 'pqrst'],
  166. ALT_SECTION:{ALT_KEYS[0]:['alt1','alt2'], ALT_KEYS[1]:[
  167. 'dir1']}, USER_HOMEDIR_SECTION:['decf']}
  168. get_os_type_method.return_value = 'redhat'
  169. find_repo_files_for_repos_method.return_value = ['abcd', 'pqrst']
  170. self.hostcleanup.do_cleanup(propertyMap)
  171. self.assertTrue(do_delete_users_method.called)
  172. self.assertTrue(do_erase_dir_silent_method.called)
  173. self.assertTrue(do_erase_files_silent_method.called)
  174. self.assertTrue(do_erase_packages_method.called)
  175. self.assertTrue(do_kill_processes_method.called)
  176. self.assertTrue(do_erase_alternatives_method.called)
  177. calls = [call(['decf']), call(['abcd', 'pqrst'])]
  178. do_erase_dir_silent_method.assert_has_calls(calls)
  179. do_erase_packages_method.assert_called_once_with(['abcd', 'pqrst'])
  180. do_erase_files_silent_method.assert_called_once_with(['abcd', 'pqrst'])
  181. do_delete_users_method.assert_called_once_with(['abcd', 'pqrst'])
  182. do_kill_processes_method.assert_called_once_with(['abcd', 'pqrst'])
  183. do_erase_alternatives_method.assert_called_once_with({ALT_KEYS[0]:['alt1',
  184. 'alt2'], ALT_KEYS[1]:['dir1']})
  185. sys.stdout = sys.__stdout__
  186. @patch.object(HostCleanup.HostCleanup, 'do_delete_by_owner')
  187. @patch.object(HostCleanup.HostCleanup, 'get_user_ids')
  188. @patch.object(HostCleanup.HostCleanup, 'do_erase_alternatives')
  189. @patch.object(HostCleanup.HostCleanup, 'find_repo_files_for_repos')
  190. @patch.object(HostCleanup.HostCleanup, 'get_os_type')
  191. @patch.object(HostCleanup.HostCleanup, 'do_kill_processes')
  192. @patch.object(HostCleanup.HostCleanup, 'do_erase_files_silent')
  193. @patch.object(HostCleanup.HostCleanup, 'do_erase_dir_silent')
  194. @patch.object(HostCleanup.HostCleanup, 'do_delete_users')
  195. @patch.object(HostCleanup.HostCleanup, 'do_erase_packages')
  196. def test_do_cleanup_default(self, do_erase_packages_method, do_delete_users_method,
  197. do_erase_dir_silent_method,
  198. do_erase_files_silent_method, do_kill_processes_method,
  199. get_os_type_method, find_repo_files_for_repos_method,
  200. do_erase_alternatives_method, get_user_ids_method,
  201. do_delete_by_owner_method):
  202. global SKIP_LIST
  203. oldSkipList = HostCleanup.SKIP_LIST
  204. HostCleanup.SKIP_LIST = ["users"]
  205. out = StringIO.StringIO()
  206. sys.stdout = out
  207. propertyMap = {PACKAGE_SECTION:['abcd', 'pqrst'], USER_SECTION:['abcd', 'pqrst'],
  208. REPO_SECTION:['abcd', 'pqrst'], DIR_SECTION:['abcd', 'pqrst'],
  209. PROCESS_SECTION:['abcd', 'pqrst'],
  210. ALT_SECTION:{ALT_KEYS[0]:['alt1','alt2'], ALT_KEYS[1]:[
  211. 'dir1']}}
  212. get_os_type_method.return_value = 'redhat'
  213. find_repo_files_for_repos_method.return_value = ['abcd', 'pqrst']
  214. self.hostcleanup.do_cleanup(propertyMap)
  215. self.assertFalse(do_delete_by_owner_method.called)
  216. self.assertFalse(get_user_ids_method.called)
  217. self.assertFalse(do_delete_users_method.called)
  218. self.assertTrue(do_erase_dir_silent_method.called)
  219. self.assertTrue(do_erase_files_silent_method.called)
  220. self.assertTrue(do_erase_packages_method.called)
  221. self.assertTrue(do_kill_processes_method.called)
  222. self.assertTrue(do_erase_alternatives_method.called)
  223. HostCleanup.SKIP_LIST = oldSkipList
  224. sys.stdout = sys.__stdout__
  225. @patch.object(HostCleanup.HostCleanup, 'find_repo_files_for_repos')
  226. @patch.object(HostCleanup.HostCleanup, 'get_os_type')
  227. @patch.object(HostCleanup.HostCleanup, 'do_kill_processes')
  228. @patch.object(HostCleanup.HostCleanup, 'do_erase_files_silent')
  229. @patch.object(HostCleanup.HostCleanup, 'do_erase_dir_silent')
  230. @patch.object(HostCleanup.HostCleanup, 'do_delete_users')
  231. @patch.object(HostCleanup.HostCleanup, 'do_erase_packages')
  232. def test_do_cleanup_with_skip(self, do_erase_packages_method,
  233. do_delete_users_method,
  234. do_erase_dir_silent_method,
  235. do_erase_files_silent_method, do_kill_processes_method,
  236. get_os_type_method, find_repo_files_for_repos_method):
  237. out = StringIO.StringIO()
  238. sys.stdout = out
  239. propertyMap = {PACKAGE_SECTION:['abcd', 'pqrst'], USER_SECTION:['abcd', 'pqrst'],
  240. REPO_SECTION:['abcd', 'pqrst'], DIR_SECTION:['abcd', 'pqrst'],
  241. PROCESS_SECTION:['abcd', 'pqrst']}
  242. get_os_type_method.return_value = 'redhat'
  243. find_repo_files_for_repos_method.return_value = ['abcd', 'pqrst']
  244. HostCleanup.SKIP_LIST = [PACKAGE_SECTION, REPO_SECTION]
  245. self.hostcleanup.do_cleanup(propertyMap)
  246. self.assertTrue(do_delete_users_method.called)
  247. self.assertTrue(do_erase_dir_silent_method.called)
  248. self.assertFalse(do_erase_files_silent_method.called)
  249. self.assertFalse(do_erase_packages_method.called)
  250. self.assertTrue(do_kill_processes_method.called)
  251. calls = [call(None), call(['abcd', 'pqrst'])]
  252. do_erase_dir_silent_method.assert_has_calls(calls)
  253. do_delete_users_method.assert_called_once_with(['abcd', 'pqrst'])
  254. do_kill_processes_method.assert_called_once_with(['abcd', 'pqrst'])
  255. sys.stdout = sys.__stdout__
  256. @patch.object(HostCleanup.HostCleanup, 'do_erase_dir_silent')
  257. @patch("os.stat")
  258. @patch("os.path.join")
  259. @patch("os.listdir")
  260. def test_do_delete_by_owner(self, listdir_mock, join_mock, stat_mock, do_erase_dir_silent_method):
  261. listdir_mock.return_value = ["k", "j"]
  262. join_mock.return_value = "path"
  263. response = MagicMock()
  264. response.st_uid = 1
  265. stat_mock.return_value = response
  266. self.hostcleanup.do_delete_by_owner([1, 2], ["a"])
  267. self.assertTrue(do_erase_dir_silent_method.called)
  268. calls = [call(["path"]), call(["path"])]
  269. do_erase_dir_silent_method.assert_has_calls(calls)
  270. @patch.object(HostCleanup.HostCleanup, 'run_os_command')
  271. def test_do_delete_users(self, run_os_command_mock):
  272. run_os_command_mock.return_value = (1, "", "")
  273. self.hostcleanup.do_delete_users(["a", "b"])
  274. self.assertTrue(run_os_command_mock.called)
  275. calls = [call('userdel -rf a'), call('userdel -rf b'), call('groupdel hadoop')]
  276. run_os_command_mock.assert_has_calls(calls)
  277. @patch("ConfigParser.RawConfigParser")
  278. @patch("__builtin__.open")
  279. @patch("os.listdir", create=True, autospec=True)
  280. def test_read_host_check_file(self, os_listdir_mock, openMock, readMock):
  281. out = StringIO.StringIO()
  282. sys.stdout = out
  283. f = MagicMock()
  284. openRead = MagicMock()
  285. openRead.read.return_value = 'java_home|hadoop'
  286. openMock.side_effect = [f, openRead]
  287. os_listdir_mock.return_value = ['111']
  288. propertyMap = self.hostcleanup.read_host_check_file('test')
  289. self.assertTrue(openMock.called)
  290. self.assertTrue(readMock.called)
  291. self.assertTrue(propertyMap.has_key(PACKAGE_SECTION))
  292. self.assertTrue(propertyMap.has_key(REPO_SECTION))
  293. self.assertTrue(propertyMap.has_key(USER_SECTION))
  294. self.assertTrue(propertyMap.has_key(DIR_SECTION))
  295. self.assertTrue(propertyMap.has_key(PROCESS_SECTION))
  296. self.assertEquals(propertyMap[PROCESS_SECTION][0], 111)
  297. sys.stdout = sys.__stdout__
  298. @patch.object(HostCleanup.HostCleanup, 'run_os_command')
  299. @patch.object(HostCleanup.HostCleanup, 'get_os_type')
  300. def test_do_earse_packages(self, get_os_type_method, run_os_command_method):
  301. out = StringIO.StringIO()
  302. sys.stdout = out
  303. get_os_type_method.return_value = 'redhat'
  304. run_os_command_method.return_value = (0, 'success', 'success')
  305. retval = self.hostcleanup.do_erase_packages(['abcd', 'wxyz'])
  306. self.assertTrue(get_os_type_method.called)
  307. self.assertTrue(run_os_command_method.called)
  308. run_os_command_method.assert_called_with("yum erase -y {0}".format(' '
  309. .join(['abcd', 'wxyz'])))
  310. self.assertEquals(0, retval)
  311. get_os_type_method.reset()
  312. run_os_command_method.reset()
  313. get_os_type_method.return_value = 'suse'
  314. run_os_command_method.return_value = (0, 'success', 'success')
  315. retval = self.hostcleanup.do_erase_packages(['abcd', 'wxyz'])
  316. self.assertTrue(get_os_type_method.called)
  317. self.assertTrue(run_os_command_method.called)
  318. run_os_command_method.assert_called_with("zypper -n -q remove {0}"
  319. .format(' '.join(['abcd', 'wxyz'])))
  320. self.assertEquals(0, retval)
  321. sys.stdout = sys.__stdout__
  322. @patch.object(HostCleanup.HostCleanup, 'get_files_in_dir')
  323. @patch.object(HostCleanup.HostCleanup, 'get_os_type')
  324. def test_find_repo_files_for_repos(self, get_os_type_method,
  325. get_files_in_dir_method):
  326. out = StringIO.StringIO()
  327. sys.stdout = out
  328. tmpfile = tempfile.mktemp()
  329. fileContent = """[###]
  330. [aass]
  331. [$$]
  332. 444]saas[333
  333. 1122[naas]2222
  334. name=sd des derft 3.1
  335. """
  336. with open(tmpfile,'w') as file:
  337. file.write(fileContent)
  338. get_os_type_method.return_value = 'redhat'
  339. get_files_in_dir_method.return_value = [ tmpfile ]
  340. repoFiles = self.hostcleanup.find_repo_files_for_repos(['aass'])
  341. self.assertTrue(get_files_in_dir_method.called)
  342. self.assertTrue(get_os_type_method.called)
  343. self.assertEquals(repoFiles, [ tmpfile ])
  344. repoFiles = self.hostcleanup.find_repo_files_for_repos(['sd des derft 3.1'])
  345. self.assertTrue(get_files_in_dir_method.called)
  346. self.assertTrue(get_os_type_method.called)
  347. self.assertEquals(repoFiles, [ tmpfile ])
  348. repoFiles = self.hostcleanup.find_repo_files_for_repos(['sd des derft 3.1', 'aass'])
  349. self.assertEquals(repoFiles, [ tmpfile ])
  350. repoFiles = self.hostcleanup.find_repo_files_for_repos(['saas'])
  351. self.assertEquals(repoFiles, [])
  352. repoFiles = self.hostcleanup.find_repo_files_for_repos([''])
  353. self.assertEquals(repoFiles, [])
  354. sys.stdout = sys.__stdout__
  355. @patch.object(HostCleanup.HostCleanup, 'run_os_command')
  356. @patch.object(HostCleanup.HostCleanup, 'do_erase_dir_silent')
  357. @patch.object(HostCleanup.HostCleanup, 'get_alternatives_desc')
  358. def test_do_erase_alternatives(self, get_alternatives_desc_mock,
  359. do_erase_dir_silent_mock, run_os_command_mock):
  360. out = StringIO.StringIO()
  361. sys.stdout = out
  362. get_alternatives_desc_mock.return_value = 'somepath to alternative\n'
  363. run_os_command_mock.return_value = (0, None, None)
  364. alt_map = {ALT_KEYS[0]:['alt1'], ALT_KEYS[1]:['dir1']}
  365. self.hostcleanup.do_erase_alternatives(alt_map)
  366. self.assertTrue(get_alternatives_desc_mock.called)
  367. get_alternatives_desc_mock.called_once_with('alt1')
  368. self.assertTrue(run_os_command_mock.called)
  369. run_os_command_mock.called_once_with(ALT_ERASE_CMD.format('alt1', 'somepath'))
  370. self.assertTrue(do_erase_dir_silent_mock.called)
  371. do_erase_dir_silent_mock.called_once_with(['dir1'])
  372. sys.stdout = sys.__stdout__
  373. if __name__ == "__main__":
  374. unittest.main()