TestMain.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  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. import StringIO
  18. import sys
  19. import unittest
  20. import logging
  21. import signal
  22. import os
  23. import socket
  24. import tempfile
  25. import platform
  26. import ConfigParser
  27. from ambari_commons import OSCheck
  28. from only_for_platform import only_for_platform, get_platform, PLATFORM_WINDOWS, PLATFORM_LINUX
  29. from mock.mock import MagicMock, patch, ANY, Mock
  30. if get_platform() != PLATFORM_WINDOWS:
  31. os_distro_value = ('Suse','11','Final')
  32. else:
  33. os_distro_value = ('win2012serverr2','6.3','WindowsServer')
  34. with patch.object(OSCheck, "os_distribution", new = MagicMock(return_value = os_distro_value)):
  35. from ambari_agent import NetUtil, security
  36. from ambari_agent import ProcessHelper, main
  37. from ambari_agent.AmbariConfig import AmbariConfig
  38. from ambari_agent.PingPortListener import PingPortListener
  39. from ambari_agent.Controller import Controller
  40. from ambari_agent.DataCleaner import DataCleaner
  41. import ambari_agent.HeartbeatHandlers as HeartbeatHandlers
  42. from ambari_commons.os_check import OSConst, OSCheck
  43. from ambari_commons.shell import shellRunner
  44. class TestMain(unittest.TestCase):
  45. def setUp(self):
  46. # disable stdout
  47. out = StringIO.StringIO()
  48. sys.stdout = out
  49. def tearDown(self):
  50. # enable stdout
  51. sys.stdout = sys.__stdout__
  52. @only_for_platform(PLATFORM_LINUX)
  53. @patch("ambari_agent.HeartbeatHandlers.HeartbeatStopHandlersLinux")
  54. @patch("os._exit")
  55. @patch("os.getpid")
  56. @patch.object(ProcessHelper, "stopAgent")
  57. def test_signal_handler(self, stopAgent_mock, os_getpid_mock, os_exit_mock, heartbeat_handler_mock):
  58. # testing exit of children
  59. main.agentPid = 4444
  60. os_getpid_mock.return_value = 5555
  61. HeartbeatHandlers.signal_handler("signum", "frame")
  62. heartbeat_handler_mock.set_stop.assert_called()
  63. os_exit_mock.reset_mock()
  64. # testing exit of main process
  65. os_getpid_mock.return_value = main.agentPid
  66. HeartbeatHandlers.signal_handler("signum", "frame")
  67. heartbeat_handler_mock.set_stop.assert_called()
  68. @patch.object(main.logger, "addHandler")
  69. @patch.object(main.logger, "setLevel")
  70. @patch("logging.handlers.RotatingFileHandler")
  71. def test_setup_logging(self, rfh_mock, setLevel_mock, addHandler_mock):
  72. # Testing silent mode
  73. main.setup_logging(False)
  74. self.assertTrue(addHandler_mock.called)
  75. setLevel_mock.assert_called_with(logging.INFO)
  76. addHandler_mock.reset_mock()
  77. setLevel_mock.reset_mock()
  78. # Testing verbose mode
  79. main.setup_logging(True)
  80. self.assertTrue(addHandler_mock.called)
  81. setLevel_mock.assert_called_with(logging.DEBUG)
  82. @patch.object(OSCheck, "os_distribution", new = MagicMock(return_value = os_distro_value))
  83. @patch.object(main.logger, "setLevel")
  84. @patch("logging.basicConfig")
  85. def test_update_log_level(self, basicConfig_mock, setLevel_mock):
  86. config = AmbariConfig().getConfig()
  87. # Testing with default setup (config file does not contain loglevel entry)
  88. # Log level should not be changed
  89. config.set('agent', 'loglevel', None)
  90. main.update_log_level(config)
  91. self.assertFalse(setLevel_mock.called)
  92. setLevel_mock.reset_mock()
  93. # Testing debug mode
  94. config.set('agent', 'loglevel', 'DEBUG')
  95. main.update_log_level(config)
  96. setLevel_mock.assert_called_with(logging.DEBUG)
  97. setLevel_mock.reset_mock()
  98. # Testing any other mode
  99. config.set('agent', 'loglevel', 'INFO')
  100. main.update_log_level(config)
  101. setLevel_mock.assert_called_with(logging.INFO)
  102. setLevel_mock.reset_mock()
  103. config.set('agent', 'loglevel', 'WRONG')
  104. main.update_log_level(config)
  105. setLevel_mock.assert_called_with(logging.INFO)
  106. @only_for_platform(PLATFORM_LINUX)
  107. @patch("signal.signal")
  108. def test_bind_signal_handlers(self, signal_mock):
  109. main.bind_signal_handlers(os.getpid())
  110. # Check if on SIGINT/SIGTERM agent is configured to terminate
  111. signal_mock.assert_any_call(signal.SIGINT, HeartbeatHandlers.signal_handler)
  112. signal_mock.assert_any_call(signal.SIGTERM, HeartbeatHandlers.signal_handler)
  113. # Check if on SIGUSR1 agent is configured to fall into debug
  114. signal_mock.assert_any_call(signal.SIGUSR1, HeartbeatHandlers.debug)
  115. @patch("os.path.exists")
  116. @patch("ConfigParser.RawConfigParser.read")
  117. def test_resolve_ambari_config(self, read_mock, exists_mock):
  118. # Trying case if conf file exists
  119. exists_mock.return_value = True
  120. main.resolve_ambari_config()
  121. self.assertTrue(read_mock.called)
  122. exists_mock.reset_mock()
  123. read_mock.reset_mock()
  124. # Trying case if conf file does not exist
  125. exists_mock.return_value = False
  126. main.resolve_ambari_config()
  127. self.assertFalse(read_mock.called)
  128. @patch.object(OSCheck, "os_distribution", new = MagicMock(return_value = os_distro_value))
  129. @patch("sys.exit")
  130. @patch("os.path.isfile")
  131. @patch("os.path.isdir")
  132. @patch("hostname.hostname")
  133. def test_perform_prestart_checks(self, hostname_mock, isdir_mock, isfile_mock, exit_mock):
  134. main.config = AmbariConfig().getConfig()
  135. # Check expected hostname test
  136. hostname_mock.return_value = "test.hst"
  137. main.perform_prestart_checks("another.hst")
  138. self.assertTrue(exit_mock.called)
  139. exit_mock.reset_mock()
  140. if OSCheck.get_os_family() != OSConst.WINSRV_FAMILY:
  141. # Trying case if there is another instance running, only valid for linux
  142. isfile_mock.return_value = True
  143. isdir_mock.return_value = True
  144. main.perform_prestart_checks(None)
  145. self.assertTrue(exit_mock.called)
  146. isfile_mock.reset_mock()
  147. isdir_mock.reset_mock()
  148. exit_mock.reset_mock()
  149. # Trying case if agent prefix dir does not exist
  150. isfile_mock.return_value = False
  151. isdir_mock.return_value = False
  152. main.perform_prestart_checks(None)
  153. self.assertTrue(exit_mock.called)
  154. isfile_mock.reset_mock()
  155. isdir_mock.reset_mock()
  156. exit_mock.reset_mock()
  157. # Trying normal case
  158. isfile_mock.return_value = False
  159. isdir_mock.return_value = True
  160. main.perform_prestart_checks(None)
  161. self.assertFalse(exit_mock.called)
  162. @only_for_platform(PLATFORM_LINUX)
  163. @patch("time.sleep")
  164. @patch.object(shellRunner,"run")
  165. @patch("os._exit")
  166. @patch("os.path.exists")
  167. def test_daemonize_and_stop(self, exists_mock, _exit_mock, kill_mock, sleep_mock):
  168. oldpid = ProcessHelper.pidfile
  169. pid = str(os.getpid())
  170. _, tmpoutfile = tempfile.mkstemp()
  171. ProcessHelper.pidfile = tmpoutfile
  172. # Test daemonization
  173. main.daemonize()
  174. saved = open(ProcessHelper.pidfile, 'r').read()
  175. self.assertEqual(pid, saved)
  176. # Reuse pid file when testing agent stop
  177. # Testing normal exit
  178. exists_mock.return_value = False
  179. main.stop_agent()
  180. kill_mock.assert_called_with(['ambari-sudo.sh', 'kill', '-15', pid])
  181. _exit_mock.assert_called_with(0)
  182. # Restore
  183. kill_mock.reset_mock()
  184. _exit_mock.reset_mock()
  185. kill_mock.return_value = {'exitCode': 0, 'output': 'out', 'error': 'err'}
  186. # Testing exit when failed to remove pid file
  187. exists_mock.return_value = True
  188. main.stop_agent()
  189. kill_mock.assert_any_call(['ambari-sudo.sh', 'kill', '-15', pid])
  190. kill_mock.assert_any_call(['ambari-sudo.sh', 'kill', '-9', pid])
  191. _exit_mock.assert_called_with(1)
  192. # Restore
  193. ProcessHelper.pidfile = oldpid
  194. os.remove(tmpoutfile)
  195. @patch("os.rmdir")
  196. @patch("os.path.join")
  197. @patch('__builtin__.open')
  198. @patch.object(ConfigParser, "ConfigParser")
  199. @patch("os._exit")
  200. @patch("os.walk")
  201. @patch("os.remove")
  202. def test_reset(self, os_remove_mock, os_walk_mock, os_exit_mock, config_parser_mock, open_mock, os_path_join_mock, os_rmdir_mock):
  203. # Agent config update
  204. config_mock = MagicMock()
  205. os_walk_mock.return_value = [('/', ('',), ('file1.txt', 'file2.txt'))]
  206. config_parser_mock.return_value= config_mock
  207. config_mock.get('server', 'hostname').return_value = "old_host"
  208. main.reset_agent(["test", "reset", "new_hostname"])
  209. self.assertEqual(config_mock.get.call_count, 3)
  210. self.assertEqual(config_mock.set.call_count, 1)
  211. self.assertEqual(os_remove_mock.call_count, 2)
  212. @patch("os.rmdir")
  213. @patch("os.path.join")
  214. @patch('__builtin__.open')
  215. @patch.object(ConfigParser, "ConfigParser")
  216. @patch("os._exit")
  217. @patch("os.walk")
  218. @patch("os.remove")
  219. def test_reset_invalid_path(self, os_remove_mock, os_walk_mock, os_exit_mock, config_parser_mock, open_mock, os_path_join_mock, os_rmdir_mock):
  220. # Agent config file cannot be accessed
  221. config_mock = MagicMock()
  222. os_walk_mock.return_value = [('/', ('',), ('file1.txt', 'file2.txt'))]
  223. config_parser_mock.return_value= config_mock
  224. config_mock.get('server', 'hostname').return_value = "old_host"
  225. open_mock.side_effect = Exception("Invalid Path!")
  226. try:
  227. main.reset_agent(["test", "reset", "new_hostname"])
  228. self.fail("Should have thrown exception!")
  229. except:
  230. self.assertTrue(True)
  231. @patch.object(OSCheck, "os_distribution", new = MagicMock(return_value = os_distro_value))
  232. @patch.object(socket, "gethostbyname")
  233. @patch.object(main, "setup_logging")
  234. @patch.object(main, "bind_signal_handlers")
  235. @patch.object(main, "stop_agent")
  236. @patch.object(AmbariConfig, "getConfigFile")
  237. @patch.object(main, "perform_prestart_checks")
  238. @patch.object(main, "daemonize")
  239. @patch.object(main, "update_log_level")
  240. @patch.object(NetUtil.NetUtil, "try_to_connect")
  241. @patch.object(Controller, "__init__")
  242. @patch.object(Controller, "start")
  243. @patch.object(Controller, "join")
  244. @patch("optparse.OptionParser.parse_args")
  245. @patch.object(DataCleaner,"start")
  246. @patch.object(DataCleaner,"__init__")
  247. @patch.object(PingPortListener,"start")
  248. @patch.object(PingPortListener,"__init__")
  249. def test_main(self, ping_port_init_mock, ping_port_start_mock, data_clean_init_mock,data_clean_start_mock,
  250. parse_args_mock, join_mock, start_mock, Controller_init_mock, try_to_connect_mock,
  251. update_log_level_mock, daemonize_mock, perform_prestart_checks_mock,
  252. ambari_config_mock,
  253. stop_mock, bind_signal_handlers_mock,
  254. setup_logging_mock, socket_mock):
  255. data_clean_init_mock.return_value = None
  256. Controller_init_mock.return_value = None
  257. ping_port_init_mock.return_value = None
  258. options = MagicMock()
  259. parse_args_mock.return_value = (options, MagicMock)
  260. try_to_connect_mock.return_value = (0, True)
  261. # use default unix config
  262. ambari_config_mock.return_value = os.path.abspath("../../../conf/unix/ambari-agent.ini")
  263. #testing call without command-line arguments
  264. main.main()
  265. self.assertTrue(setup_logging_mock.called)
  266. self.assertTrue(bind_signal_handlers_mock.called)
  267. if OSCheck.get_os_family() != OSConst.WINSRV_FAMILY:
  268. self.assertTrue(stop_mock.called)
  269. #self.assertTrue(resolve_ambari_config_mock.called)
  270. self.assertTrue(perform_prestart_checks_mock.called)
  271. if OSCheck.get_os_family() != OSConst.WINSRV_FAMILY:
  272. self.assertTrue(daemonize_mock.called)
  273. self.assertTrue(update_log_level_mock.called)
  274. try_to_connect_mock.assert_called_once_with(ANY, -1, ANY)
  275. self.assertTrue(start_mock.called)
  276. self.assertTrue(data_clean_init_mock.called)
  277. self.assertTrue(data_clean_start_mock.called)
  278. self.assertTrue(ping_port_init_mock.called)
  279. self.assertTrue(ping_port_start_mock.called)
  280. perform_prestart_checks_mock.reset_mock()
  281. # Testing call with --expected-hostname parameter
  282. options.expected_hostname = "test.hst"
  283. main.main()
  284. perform_prestart_checks_mock.assert_called_once_with(options.expected_hostname)