TestBootstrap.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879
  1. '''
  2. Licensed to the Apache Software Foundation (ASF) under one
  3. or more contributor license agreements. See the NOTICE file
  4. distributed with this work for additional information
  5. regarding copyright ownership. The ASF licenses this file
  6. to you under the Apache License, Version 2.0 (the
  7. "License"); you may not use this file except in compliance
  8. with the License. You may obtain a copy of the License at
  9. http://www.apache.org/licenses/LICENSE-2.0
  10. Unless required by applicable law or agreed to in writing, software
  11. distributed under the License is distributed on an "AS IS" BASIS,
  12. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. See the License for the specific language governing permissions and
  14. limitations under the License.
  15. '''
  16. from stacks.utils.RMFTestCase import *
  17. import bootstrap
  18. import time
  19. import subprocess
  20. import os
  21. import logging
  22. import tempfile
  23. import pprint
  24. from ambari_commons.os_check import OSCheck
  25. from bootstrap import PBootstrap, Bootstrap, BootstrapDefault, SharedState, HostLog, SCP, SSH
  26. from unittest import TestCase
  27. from subprocess import Popen
  28. from bootstrap import AMBARI_PASSPHRASE_VAR_NAME
  29. from mock.mock import MagicMock, call
  30. from mock.mock import patch
  31. from mock.mock import create_autospec
  32. from only_for_platform import not_for_platform, os_distro_value, PLATFORM_WINDOWS
  33. @not_for_platform(PLATFORM_WINDOWS)
  34. class TestBootstrap(TestCase):
  35. def setUp(self):
  36. logging.basicConfig(level=logging.ERROR)
  37. def test_getRemoteName(self):
  38. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  39. "setupAgentFile", "ambariServer", "centos6", None, "8440", "root")
  40. res = bootstrap_obj = Bootstrap("hostname", shared_state)
  41. utime1 = 1234
  42. utime2 = 12345
  43. bootstrap_obj.getUtime = MagicMock(return_value=utime1)
  44. remote1 = bootstrap_obj.getRemoteName("/tmp/setupAgent.sh")
  45. self.assertEquals(remote1, "/tmp/setupAgent{0}.sh".format(utime1))
  46. bootstrap_obj.getUtime.return_value=utime2
  47. remote1 = bootstrap_obj.getRemoteName("/tmp/setupAgent.sh")
  48. self.assertEquals(remote1, "/tmp/setupAgent{0}.sh".format(utime1))
  49. remote2 = bootstrap_obj.getRemoteName("/tmp/host_pass")
  50. self.assertEquals(remote2, "/tmp/host_pass{0}".format(utime2))
  51. # TODO: Test bootstrap timeout
  52. # TODO: test_return_error_message_for_missing_sudo_package
  53. def test_getAmbariPort(self):
  54. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  55. "setupAgentFile", "ambariServer", "centos6",
  56. None, "8440", "root")
  57. bootstrap_obj = Bootstrap("hostname", shared_state)
  58. self.assertEquals(bootstrap_obj.getAmbariPort(),"8440")
  59. shared_state.server_port = None
  60. bootstrap_obj = Bootstrap("hostname", shared_state)
  61. self.assertEquals(bootstrap_obj.getAmbariPort(),"null")
  62. @patch.object(subprocess, "Popen")
  63. @patch("sys.stderr")
  64. @patch("sys.exit")
  65. @patch.object(PBootstrap, "run")
  66. @patch("os.path.dirname")
  67. @patch("os.path.realpath")
  68. def test_bootstrap_main(self, dirname_mock, realpath_mock, run_mock, exit_mock, stderr_mock, subprocess_Popen_mock):
  69. bootstrap.main(["bootstrap.py", "hostname,hostname2", "/tmp/bootstrap", "root", "sshkey_file", "setupAgent.py", "ambariServer", \
  70. "centos6", "1.1.1", "8440", "root", "passwordfile"])
  71. self.assertTrue(run_mock.called)
  72. run_mock.reset_mock()
  73. bootstrap.main(["bootstrap.py", "hostname,hostname2", "/tmp/bootstrap", "root", "sshkey_file", "setupAgent.py", "ambariServer", \
  74. "centos6", "1.1.1", "8440", "root", None])
  75. self.assertTrue(run_mock.called)
  76. run_mock.reset_mock()
  77. def side_effect(retcode):
  78. raise Exception(retcode, "sys.exit")
  79. exit_mock.side_effect = side_effect
  80. try:
  81. bootstrap.main(["bootstrap.py","hostname,hostname2", "/tmp/bootstrap"])
  82. self.fail("sys.exit(2)")
  83. except Exception:
  84. # Expected
  85. pass
  86. self.assertTrue(exit_mock.called)
  87. @patch("os.environ")
  88. def test_getRunSetupWithPasswordCommand(self, environ_mock):
  89. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  90. "setupAgentFile", "ambariServer", "centos6",
  91. None, "8440", "root")
  92. environ_mock.__getitem__.return_value = "TEST_PASSPHRASE"
  93. bootstrap_obj = Bootstrap("hostname", shared_state)
  94. utime = 1234
  95. bootstrap_obj.getUtime = MagicMock(return_value=utime)
  96. ret = bootstrap_obj.getRunSetupWithPasswordCommand("hostname")
  97. expected = "sudo -S python /var/lib/ambari-agent/tmp/setupAgent{0}.py hostname TEST_PASSPHRASE " \
  98. "ambariServer root 8440 < /var/lib/ambari-agent/tmp/host_pass{0}".format(utime)
  99. self.assertEquals(ret, expected)
  100. def test_generateRandomFileName(self):
  101. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  102. "setupAgentFile", "ambariServer", "centos6",
  103. None, "8440", "root")
  104. bootstrap_obj = Bootstrap("hostname", shared_state)
  105. self.assertTrue(bootstrap_obj.generateRandomFileName(None) == bootstrap_obj.getUtime())
  106. @patch.object(OSCheck, "is_redhat_family")
  107. @patch.object(OSCheck, "is_suse_family")
  108. def test_getRepoDir(self, is_suse_family, is_redhat_family):
  109. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  110. "setupAgentFile", "ambariServer", "centos6",
  111. None, "8440", "root")
  112. bootstrap_obj = Bootstrap("hostname", shared_state)
  113. # Suse
  114. is_redhat_family.return_value = False
  115. is_suse_family.return_value = True
  116. res = bootstrap_obj.getRepoDir()
  117. self.assertEquals(res, "/etc/zypp/repos.d")
  118. # non-Suse
  119. is_suse_family.return_value = False
  120. is_redhat_family.return_value = True
  121. res = bootstrap_obj.getRepoDir()
  122. self.assertEquals(res, "/etc/yum.repos.d")
  123. def test_getSetupScript(self):
  124. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  125. "setupAgentFile", "ambariServer", "centos6",
  126. None, "8440", "root")
  127. bootstrap_obj = Bootstrap("hostname", shared_state)
  128. self.assertEquals(bootstrap_obj.shared_state.script_dir, "scriptDir")
  129. def test_run_setup_agent_command_ends_with_project_version(self):
  130. os.environ[AMBARI_PASSPHRASE_VAR_NAME] = ""
  131. version = "1.1.1"
  132. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  133. "setupAgentFile", "ambariServer", "centos6",
  134. version, "8440", "root")
  135. bootstrap_obj = Bootstrap("hostname", shared_state)
  136. runSetupCommand = bootstrap_obj.getRunSetupCommand("hostname")
  137. self.assertTrue(runSetupCommand.endswith(version + " 8440"))
  138. def test_agent_setup_command_without_project_version(self):
  139. os.environ[AMBARI_PASSPHRASE_VAR_NAME] = ""
  140. version = None
  141. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  142. "setupAgentFile", "ambariServer", "centos6",
  143. version, "8440", "root")
  144. bootstrap_obj = Bootstrap("hostname", shared_state)
  145. runSetupCommand = bootstrap_obj.getRunSetupCommand("hostname")
  146. self.assertTrue(runSetupCommand.endswith(" 8440"))
  147. # TODO: test_os_check_fail_fails_bootstrap_execution
  148. def test_host_log(self):
  149. tmp_file, tmp_filename = tempfile.mkstemp()
  150. dummy_log = HostLog(tmp_filename)
  151. # First write to log
  152. dummy_log.write("a\nb\nc")
  153. # Read it
  154. with open(tmp_filename) as f:
  155. s = f.read()
  156. etalon = "a\nb\nc\n"
  157. self.assertEquals(s, etalon)
  158. # Next write
  159. dummy_log.write("Yet another string")
  160. # Read it
  161. with open(tmp_filename) as f:
  162. s = f.read()
  163. etalon = "a\nb\nc\nYet another string\n"
  164. self.assertEquals(s, etalon)
  165. # Should not append line end if it already exists
  166. dummy_log.write("line break->\n")
  167. # Read it
  168. with open(tmp_filename) as f:
  169. s = f.read()
  170. etalon = "a\nb\nc\nYet another string\nline break->\n"
  171. self.assertEquals(s, etalon)
  172. # Cleanup
  173. os.unlink(tmp_filename)
  174. @patch("subprocess.Popen")
  175. def test_SCP(self, popenMock):
  176. params = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  177. "setupAgentFile", "ambariServer", "centos6",
  178. "1.2.1", "8440", "root")
  179. host_log_mock = MagicMock()
  180. log = {'text': ""}
  181. def write_side_effect(text):
  182. log['text'] = log['text'] + text
  183. host_log_mock.write.side_effect = write_side_effect
  184. scp = SCP(params.user, params.sshkey_file, "dummy-host", "src/file",
  185. "dst/file", params.bootdir, host_log_mock)
  186. log_sample = "log_sample"
  187. error_sample = "error_sample"
  188. # Successful run
  189. process = MagicMock()
  190. popenMock.return_value = process
  191. process.communicate.return_value = (log_sample, error_sample)
  192. process.returncode = 0
  193. retcode = scp.run()
  194. self.assertTrue(popenMock.called)
  195. self.assertTrue(log_sample in log['text'])
  196. self.assertTrue(error_sample in log['text'])
  197. command_str = str(popenMock.call_args[0][0])
  198. self.assertEquals(command_str, "['scp', '-r', '-o', 'ConnectTimeout=60', '-o', "
  199. "'BatchMode=yes', '-o', 'StrictHostKeyChecking=no', '-i', 'sshkey_file',"
  200. " 'src/file', 'root@dummy-host:dst/file']")
  201. self.assertEqual(retcode["exitstatus"], 0)
  202. log['text'] = ""
  203. #unsuccessfull run
  204. process.returncode = 1
  205. retcode = scp.run()
  206. self.assertTrue(log_sample in log['text'])
  207. self.assertTrue(error_sample in log['text'])
  208. self.assertEqual(retcode["exitstatus"], 1)
  209. @patch("subprocess.Popen")
  210. def test_SSH(self, popenMock):
  211. params = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  212. "setupAgentFile", "ambariServer", "centos6",
  213. "1.2.1", "8440", "root")
  214. host_log_mock = MagicMock()
  215. log = {'text': ""}
  216. def write_side_effect(text):
  217. log['text'] = log['text'] + text
  218. host_log_mock.write.side_effect = write_side_effect
  219. ssh = SSH(params.user, params.sshkey_file, "dummy-host", "dummy-command",
  220. params.bootdir, host_log_mock)
  221. log_sample = "log_sample"
  222. error_sample = "error_sample"
  223. # Successful run
  224. process = MagicMock()
  225. popenMock.return_value = process
  226. process.communicate.return_value = (log_sample, error_sample)
  227. process.returncode = 0
  228. retcode = ssh.run()
  229. self.assertTrue(popenMock.called)
  230. self.assertTrue(log_sample in log['text'])
  231. self.assertTrue(error_sample in log['text'])
  232. command_str = str(popenMock.call_args[0][0])
  233. self.assertEquals(command_str, "['ssh', '-o', 'ConnectTimeOut=60', '-o', "
  234. "'StrictHostKeyChecking=no', '-o', 'BatchMode=yes', '-tt', '-i', "
  235. "'sshkey_file', 'root@dummy-host', 'dummy-command']")
  236. self.assertEqual(retcode["exitstatus"], 0)
  237. log['text'] = ""
  238. #unsuccessfull run
  239. process.returncode = 1
  240. retcode = ssh.run()
  241. self.assertTrue(log_sample in log['text'])
  242. self.assertTrue(error_sample in log['text'])
  243. self.assertEqual(retcode["exitstatus"], 1)
  244. log['text'] = ""
  245. # unsuccessful run with error message
  246. process.returncode = 1
  247. dummy_error_message = "dummy_error_message"
  248. ssh = SSH(params.user, params.sshkey_file, "dummy-host", "dummy-command",
  249. params.bootdir, host_log_mock, errorMessage= dummy_error_message)
  250. retcode = ssh.run()
  251. self.assertTrue(log_sample in log['text'])
  252. self.assertTrue(error_sample in log['text'])
  253. self.assertTrue(dummy_error_message in log['text'])
  254. self.assertEqual(retcode["exitstatus"], 1)
  255. def test_getOsCheckScript(self):
  256. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  257. "setupAgentFile", "ambariServer", "centos6",
  258. None, "8440", "root")
  259. bootstrap_obj = Bootstrap("hostname", shared_state)
  260. ocs = bootstrap_obj.getOsCheckScript()
  261. self.assertEquals(ocs, "scriptDir/os_check_type.py")
  262. @patch.object(BootstrapDefault, "getRemoteName")
  263. def test_getOsCheckScriptRemoteLocation(self, getRemoteName_mock):
  264. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  265. "setupAgentFile", "ambariServer", "centos6",
  266. None, "8440", "root")
  267. bootstrap_obj = Bootstrap("hostname", shared_state)
  268. v = "/tmp/os_check_type1374259902.py"
  269. getRemoteName_mock.return_value = v
  270. ocs = bootstrap_obj.getOsCheckScriptRemoteLocation()
  271. self.assertEquals(ocs, v)
  272. @patch.object(BootstrapDefault, "is_suse")
  273. def test_getRepoFile(self, is_suse_mock):
  274. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  275. "setupAgentFile", "ambariServer", "centos6",
  276. None, "8440", "root")
  277. bootstrap_obj = Bootstrap("hostname", shared_state)
  278. is_suse_mock.return_value = False
  279. rf = bootstrap_obj.getRepoFile()
  280. self.assertEquals(rf, "/etc/yum.repos.d/ambari.repo")
  281. @patch.object(SSH, "__init__")
  282. @patch.object(SSH, "run")
  283. @patch.object(HostLog, "write")
  284. def test_createTargetDir(self, write_mock, run_mock,
  285. init_mock):
  286. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  287. "setupAgentFile", "ambariServer", "centos6",
  288. None, "8440", "root")
  289. bootstrap_obj = Bootstrap("hostname", shared_state)
  290. expected = 42
  291. init_mock.return_value = None
  292. run_mock.return_value = expected
  293. res = bootstrap_obj.createTargetDir()
  294. self.assertEquals(res, expected)
  295. command = str(init_mock.call_args[0][3])
  296. self.assertEqual(command,
  297. "sudo mkdir -p /var/lib/ambari-agent/tmp ; "
  298. "sudo chown -R root /var/lib/ambari-agent/tmp ; "
  299. "sudo chmod 755 /var/lib/ambari-agent ; "
  300. "sudo chmod 755 /var/lib/ambari-agent/data ; "
  301. "sudo chmod 777 /var/lib/ambari-agent/tmp")
  302. @patch.object(BootstrapDefault, "getOsCheckScript")
  303. @patch.object(BootstrapDefault, "getOsCheckScriptRemoteLocation")
  304. @patch.object(SCP, "__init__")
  305. @patch.object(SCP, "run")
  306. @patch.object(HostLog, "write")
  307. def test_copyOsCheckScript(self, write_mock, run_mock, init_mock,
  308. getOsCheckScriptRemoteLocation_mock, getOsCheckScript_mock):
  309. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  310. "setupAgentFile", "ambariServer", "centos6",
  311. None, "8440", "root")
  312. bootstrap_obj = Bootstrap("hostname", shared_state)
  313. getOsCheckScript_mock.return_value = "OsCheckScript"
  314. getOsCheckScriptRemoteLocation_mock.return_value = "OsCheckScriptRemoteLocation"
  315. expected = 42
  316. init_mock.return_value = None
  317. run_mock.return_value = expected
  318. res = bootstrap_obj.copyOsCheckScript()
  319. self.assertEquals(res, expected)
  320. input_file = str(init_mock.call_args[0][3])
  321. remote_file = str(init_mock.call_args[0][4])
  322. self.assertEqual(input_file, "OsCheckScript")
  323. self.assertEqual(remote_file, "OsCheckScriptRemoteLocation")
  324. @patch.object(BootstrapDefault, "getRemoteName")
  325. @patch.object(BootstrapDefault, "hasPassword")
  326. @patch.object(OSCheck, "is_suse_family")
  327. @patch.object(OSCheck, "is_ubuntu_family")
  328. @patch.object(OSCheck, "is_redhat_family")
  329. def test_getRepoFile(self, is_redhat_family, is_ubuntu_family, is_suse_family, hasPassword_mock, getRemoteName_mock):
  330. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  331. "setupAgentFile", "ambariServer", "centos6",
  332. None, "8440", "root")
  333. is_redhat_family.return_value = True
  334. is_ubuntu_family.return_value = False
  335. is_suse_family.return_value = False
  336. bootstrap_obj = Bootstrap("hostname", shared_state)
  337. # Without password
  338. hasPassword_mock.return_value = False
  339. getRemoteName_mock.return_value = "RemoteName"
  340. rf = bootstrap_obj.getMoveRepoFileCommand("target")
  341. self.assertEquals(rf, "sudo mv RemoteName target/ambari.repo")
  342. # With password
  343. hasPassword_mock.return_value = True
  344. getRemoteName_mock.return_value = "RemoteName"
  345. rf = bootstrap_obj.getMoveRepoFileCommand("target")
  346. self.assertEquals(rf, "sudo -S mv RemoteName target/ambari.repo < RemoteName")
  347. @patch("os.path.exists")
  348. @patch.object(OSCheck, "is_suse_family")
  349. @patch.object(OSCheck, "is_ubuntu_family")
  350. @patch.object(OSCheck, "is_redhat_family")
  351. @patch.object(BootstrapDefault, "getMoveRepoFileCommand")
  352. @patch.object(BootstrapDefault, "getRepoDir")
  353. @patch.object(BootstrapDefault, "getRepoFile")
  354. @patch.object(BootstrapDefault, "getRemoteName")
  355. @patch.object(SCP, "__init__")
  356. @patch.object(SCP, "run")
  357. @patch.object(SSH, "__init__")
  358. @patch.object(SSH, "run")
  359. @patch.object(HostLog, "write")
  360. def test_copyNeededFiles(self, write_mock, ssh_run_mock, ssh_init_mock,
  361. scp_run_mock, scp_init_mock,
  362. getRemoteName_mock, getRepoFile_mock, getRepoDir,
  363. getMoveRepoFileCommand, is_redhat_family, is_ubuntu_family, is_suse_family,
  364. os_path_exists_mock):
  365. #
  366. # Ambari repo file exists
  367. #
  368. def os_path_exists_side_effect(*args, **kwargs):
  369. if args[0] == getRepoFile_mock():
  370. return True
  371. else:
  372. return False
  373. os_path_exists_mock.side_effect = os_path_exists_side_effect
  374. os_path_exists_mock.return_value = None
  375. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  376. "setupAgentFile", "ambariServer", "centos6",
  377. None, "8440", "root")
  378. is_redhat_family.return_value = True
  379. is_ubuntu_family.return_value = False
  380. is_suse_family.return_value = False
  381. bootstrap_obj = Bootstrap("hostname", shared_state)
  382. getMoveRepoFileCommand.return_value = "MoveRepoFileCommand"
  383. getRepoDir.return_value = "RepoDir"
  384. getRemoteName_mock.return_value = "RemoteName"
  385. getRepoFile_mock.return_value = "RepoFile"
  386. expected1 = {"exitstatus": 42, "log": "log42", "errormsg": "errorMsg"}
  387. expected2 = {"exitstatus": 17, "log": "log17", "errormsg": "errorMsg"}
  388. expected3 = {"exitstatus": 1, "log": "log1", "errormsg": "errorMsg"}
  389. expected4 = {"exitstatus": 17, "log": "log17", "errormsg": "errorMsg"}
  390. scp_init_mock.return_value = None
  391. ssh_init_mock.return_value = None
  392. # Testing max retcode return
  393. scp_run_mock.side_effect = [expected1, expected3]
  394. ssh_run_mock.side_effect = [expected2, expected4]
  395. res = bootstrap_obj.copyNeededFiles()
  396. self.assertEquals(res, expected1["exitstatus"])
  397. input_file = str(scp_init_mock.call_args[0][3])
  398. remote_file = str(scp_init_mock.call_args[0][4])
  399. self.assertEqual(input_file, "setupAgentFile")
  400. self.assertEqual(remote_file, "RemoteName")
  401. command = str(ssh_init_mock.call_args[0][3])
  402. self.assertEqual(command, "sudo chmod 644 RepoFile")
  403. # Another order
  404. expected1 = {"exitstatus": 0, "log": "log0", "errormsg": "errorMsg"}
  405. expected2 = {"exitstatus": 17, "log": "log17", "errormsg": "errorMsg"}
  406. expected3 = {"exitstatus": 1, "log": "log1", "errormsg": "errorMsg"}
  407. expected4 = {"exitstatus": 17, "log": "log17", "errormsg": "errorMsg"}
  408. scp_run_mock.side_effect = [expected1, expected3]
  409. ssh_run_mock.side_effect = [expected2, expected4]
  410. res = bootstrap_obj.copyNeededFiles()
  411. self.assertEquals(res, expected2["exitstatus"])
  412. # yet another order
  413. expected1 = {"exitstatus": 33, "log": "log33", "errormsg": "errorMsg"}
  414. expected2 = {"exitstatus": 17, "log": "log17", "errormsg": "errorMsg"}
  415. expected3 = {"exitstatus": 42, "log": "log42", "errormsg": "errorMsg"}
  416. expected4 = {"exitstatus": 17, "log": "log17", "errormsg": "errorMsg"}
  417. scp_run_mock.side_effect = [expected1, expected3]
  418. ssh_run_mock.side_effect = [expected2, expected4]
  419. res = bootstrap_obj.copyNeededFiles()
  420. self.assertEquals(res, expected3["exitstatus"])
  421. #
  422. #Ambari repo file does not exist
  423. #
  424. os_path_exists_mock.side_effect = None
  425. os_path_exists_mock.return_value = False
  426. #Expectations:
  427. # SSH will not be called at all
  428. # SCP will be called once for copying the setup script file
  429. scp_run_mock.reset_mock()
  430. ssh_run_mock.reset_mock()
  431. expectedResult = {"exitstatus": 33, "log": "log33", "errormsg": "errorMsg"}
  432. scp_run_mock.side_effect = [expectedResult]
  433. res = bootstrap_obj.copyNeededFiles()
  434. self.assertFalse(ssh_run_mock.called)
  435. self.assertEquals(res, expectedResult["exitstatus"])
  436. @patch.object(BootstrapDefault, "getOsCheckScriptRemoteLocation")
  437. @patch.object(SSH, "__init__")
  438. @patch.object(SSH, "run")
  439. @patch.object(HostLog, "write")
  440. def test_runOsCheckScript(self, write_mock, run_mock,
  441. init_mock, getOsCheckScriptRemoteLocation_mock):
  442. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  443. "setupAgentFile", "ambariServer", "centos6",
  444. None, "8440", "root")
  445. bootstrap_obj = Bootstrap("hostname", shared_state)
  446. getOsCheckScriptRemoteLocation_mock.return_value = "OsCheckScriptRemoteLocation"
  447. expected = 42
  448. init_mock.return_value = None
  449. run_mock.return_value = expected
  450. res = bootstrap_obj.runOsCheckScript()
  451. self.assertEquals(res, expected)
  452. command = str(init_mock.call_args[0][3])
  453. self.assertEqual(command,
  454. "chmod a+x OsCheckScriptRemoteLocation && "
  455. "env PYTHONPATH=$PYTHONPATH:/var/lib/ambari-agent/tmp OsCheckScriptRemoteLocation centos6")
  456. @patch.object(SSH, "__init__")
  457. @patch.object(BootstrapDefault, "getRunSetupCommand")
  458. @patch.object(SSH, "run")
  459. @patch.object(HostLog, "write")
  460. def test_runSetupAgent(self, write_mock, run_mock,
  461. getRunSetupCommand_mock, init_mock):
  462. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  463. "setupAgentFile", "ambariServer", "centos6",
  464. None, "8440", "root")
  465. bootstrap_obj = Bootstrap("hostname", shared_state)
  466. getRunSetupCommand_mock.return_value = "RunSetupCommand"
  467. expected = 42
  468. init_mock.return_value = None
  469. run_mock.return_value = expected
  470. res = bootstrap_obj.runSetupAgent()
  471. self.assertEquals(res, expected)
  472. command = str(init_mock.call_args[0][3])
  473. self.assertEqual(command, "RunSetupCommand")
  474. @patch.object(BootstrapDefault, "hasPassword")
  475. @patch.object(BootstrapDefault, "getRunSetupWithPasswordCommand")
  476. @patch.object(BootstrapDefault, "getRunSetupWithoutPasswordCommand")
  477. def test_getRunSetupCommand(self, getRunSetupWithoutPasswordCommand_mock,
  478. getRunSetupWithPasswordCommand_mock,
  479. hasPassword_mock):
  480. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  481. "setupAgentFile", "ambariServer", "centos6",
  482. None, "8440", "root")
  483. bootstrap_obj = Bootstrap("hostname", shared_state)
  484. # With password
  485. hasPassword_mock.return_value = True
  486. getRunSetupWithPasswordCommand_mock.return_value = "RunSetupWithPasswordCommand"
  487. getRunSetupWithoutPasswordCommand_mock.return_value = "RunSetupWithoutPasswordCommand"
  488. res = bootstrap_obj.getRunSetupCommand("dummy-host")
  489. self.assertEqual(res, "RunSetupWithPasswordCommand")
  490. # Without password
  491. hasPassword_mock.return_value = False
  492. res = bootstrap_obj.getRunSetupCommand("dummy-host")
  493. self.assertEqual(res, "RunSetupWithoutPasswordCommand")
  494. @patch.object(HostLog, "write")
  495. def test_createDoneFile(self, write_mock):
  496. tmp_dir = tempfile.gettempdir()
  497. shared_state = SharedState("root", "sshkey_file", "scriptDir", tmp_dir,
  498. "setupAgentFile", "ambariServer", "centos6",
  499. None, "8440", "root")
  500. bootstrap_obj = Bootstrap("hostname", shared_state)
  501. done_file = os.path.join(tmp_dir, "hostname.done")
  502. expected = 42
  503. bootstrap_obj.createDoneFile(expected)
  504. with open(done_file) as df:
  505. res = df.read()
  506. self.assertEqual(res, str(expected))
  507. os.unlink(done_file)
  508. @patch.object(OSCheck, "is_suse_family")
  509. @patch.object(OSCheck, "is_ubuntu_family")
  510. @patch.object(OSCheck, "is_redhat_family")
  511. @patch.object(SSH, "__init__")
  512. @patch.object(SSH, "run")
  513. @patch.object(HostLog, "write")
  514. def test_checkSudoPackage(self, write_mock, run_mock, init_mock, is_redhat_family, is_ubuntu_family, is_suse_family):
  515. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  516. "setupAgentFile", "ambariServer", "centos6",
  517. None, "8440", "root")
  518. bootstrap_obj = Bootstrap("hostname", shared_state)
  519. expected = 42
  520. init_mock.return_value = None
  521. run_mock.return_value = expected
  522. is_redhat_family.return_value = True
  523. is_ubuntu_family.return_value = False
  524. is_suse_family.return_value = False
  525. res = bootstrap_obj.checkSudoPackage()
  526. self.assertEquals(res, expected)
  527. command = str(init_mock.call_args[0][3])
  528. self.assertEqual(command, "rpm -qa | grep -e '^sudo\-'")
  529. @patch.object(OSCheck, "is_suse_family")
  530. @patch.object(OSCheck, "is_ubuntu_family")
  531. @patch.object(OSCheck, "is_redhat_family")
  532. @patch.object(SSH, "__init__")
  533. @patch.object(SSH, "run")
  534. @patch.object(HostLog, "write")
  535. def test_checkSudoPackageUbuntu(self, write_mock, run_mock, init_mock,
  536. is_redhat_family, is_ubuntu_family, is_suse_family):
  537. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  538. "setupAgentFile", "ambariServer", "ubuntu12",
  539. None, "8440", "root")
  540. is_redhat_family.return_value = False
  541. is_ubuntu_family.return_value = True
  542. is_suse_family.return_value = False
  543. bootstrap_obj = Bootstrap("hostname", shared_state)
  544. expected = 42
  545. init_mock.return_value = None
  546. run_mock.return_value = expected
  547. res = bootstrap_obj.checkSudoPackage()
  548. self.assertEquals(res, expected)
  549. command = str(init_mock.call_args[0][3])
  550. self.assertEqual(command, "dpkg --get-selections|grep -e '^sudo\s*install'")
  551. @patch.object(SSH, "__init__")
  552. @patch.object(SSH, "run")
  553. @patch.object(HostLog, "write")
  554. @patch.object(BootstrapDefault, "getPasswordFile")
  555. def test_deletePasswordFile(self, getPasswordFile_mock, write_mock, run_mock,
  556. init_mock):
  557. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  558. "setupAgentFile", "ambariServer", "centos6",
  559. None, "8440", "root")
  560. bootstrap_obj = Bootstrap("hostname", shared_state)
  561. expected = 42
  562. getPasswordFile_mock.return_value = "PasswordFile"
  563. init_mock.return_value = None
  564. run_mock.return_value = expected
  565. res = bootstrap_obj.deletePasswordFile()
  566. self.assertEquals(res, expected)
  567. command = str(init_mock.call_args[0][3])
  568. self.assertEqual(command, "rm PasswordFile")
  569. @patch.object(BootstrapDefault, "getPasswordFile")
  570. @patch.object(SCP, "__init__")
  571. @patch.object(SCP, "run")
  572. @patch.object(SSH, "__init__")
  573. @patch.object(SSH, "run")
  574. @patch.object(HostLog, "write")
  575. def test_copyPasswordFile(self, write_mock, ssh_run_mock,
  576. ssh_init_mock, scp_run_mock,
  577. scp_init_mock, getPasswordFile_mock):
  578. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  579. "setupAgentFile", "ambariServer", "centos6",
  580. None, "8440", "root", password_file="PasswordFile")
  581. bootstrap_obj = Bootstrap("hostname", shared_state)
  582. getPasswordFile_mock.return_value = "PasswordFile"
  583. # Testing max retcode return
  584. expected1 = {"exitstatus": 42, "log": "log42", "errormsg": "errorMsg"}
  585. expected2 = {"exitstatus": 17, "log": "log17", "errormsg": "errorMsg"}
  586. scp_init_mock.return_value = None
  587. scp_run_mock.return_value = expected1
  588. ssh_init_mock.return_value = None
  589. ssh_run_mock.return_value = expected2
  590. res = bootstrap_obj.copyPasswordFile()
  591. self.assertEquals(res, expected1["exitstatus"])
  592. input_file = str(scp_init_mock.call_args[0][3])
  593. remote_file = str(scp_init_mock.call_args[0][4])
  594. self.assertEqual(input_file, "PasswordFile")
  595. self.assertEqual(remote_file, "PasswordFile")
  596. command = str(ssh_init_mock.call_args[0][3])
  597. self.assertEqual(command, "chmod 600 PasswordFile")
  598. # Another order
  599. expected1 = {"exitstatus": 0, "log": "log0", "errormsg": "errorMsg"}
  600. expected2 = {"exitstatus": 17, "log": "log17", "errormsg": "errorMsg"}
  601. scp_run_mock.return_value = expected1
  602. ssh_run_mock.return_value = expected2
  603. @patch.object(SSH, "__init__")
  604. @patch.object(SSH, "run")
  605. @patch.object(HostLog, "write")
  606. @patch.object(BootstrapDefault, "getPasswordFile")
  607. def test_changePasswordFileModeOnHost(self, getPasswordFile_mock, write_mock,
  608. run_mock, init_mock):
  609. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  610. "setupAgentFile", "ambariServer", "centos6",
  611. None, "8440", "root")
  612. bootstrap_obj = Bootstrap("hostname", shared_state)
  613. expected = 42
  614. getPasswordFile_mock.return_value = "PasswordFile"
  615. init_mock.return_value = None
  616. run_mock.return_value = expected
  617. res = bootstrap_obj.changePasswordFileModeOnHost()
  618. self.assertEquals(res, expected)
  619. command = str(init_mock.call_args[0][3])
  620. self.assertEqual(command, "chmod 600 PasswordFile")
  621. @patch.object(HostLog, "write")
  622. def test_try_to_execute(self, write_mock):
  623. expected = 43
  624. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  625. "setupAgentFile", "ambariServer", "centos6",
  626. None, "8440", "root")
  627. bootstrap_obj = Bootstrap("hostname", shared_state)
  628. # Normal case
  629. def act_normal_return_int():
  630. return 43
  631. ret = bootstrap_obj.try_to_execute(act_normal_return_int)
  632. self.assertEqual(ret["exitstatus"], expected)
  633. self.assertFalse(write_mock.called)
  634. write_mock.reset_mock()
  635. def act_normal_return():
  636. return {"exitstatus": 43}
  637. ret = bootstrap_obj.try_to_execute(act_normal_return)
  638. self.assertEqual(ret["exitstatus"], expected)
  639. self.assertFalse(write_mock.called)
  640. write_mock.reset_mock()
  641. # Exception scenario
  642. def act():
  643. raise IOError()
  644. ret = bootstrap_obj.try_to_execute(act)
  645. self.assertEqual(ret["exitstatus"], 177)
  646. self.assertTrue(write_mock.called)
  647. @patch.object(BootstrapDefault, "try_to_execute")
  648. @patch.object(BootstrapDefault, "hasPassword")
  649. @patch.object(BootstrapDefault, "createDoneFile")
  650. @patch.object(HostLog, "write")
  651. @patch("logging.warn")
  652. @patch("logging.error")
  653. def test_run(self, error_mock, warn_mock, write_mock, createDoneFile_mock,
  654. hasPassword_mock, try_to_execute_mock):
  655. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  656. "setupAgentFile", "ambariServer", "centos6",
  657. None, "8440", "root")
  658. bootstrap_obj = Bootstrap("hostname", shared_state)
  659. # Testing workflow without password
  660. bootstrap_obj.copied_password_file = False
  661. hasPassword_mock.return_value = False
  662. try_to_execute_mock.return_value = {"exitstatus": 0, "log":"log0", "errormsg":"errormsg0"}
  663. bootstrap_obj.run()
  664. self.assertEqual(try_to_execute_mock.call_count, 7) # <- Adjust if changed
  665. self.assertTrue(createDoneFile_mock.called)
  666. self.assertEqual(bootstrap_obj.getStatus()["return_code"], 0)
  667. try_to_execute_mock.reset_mock()
  668. createDoneFile_mock.reset_mock()
  669. # Testing workflow with password
  670. bootstrap_obj.copied_password_file = True
  671. hasPassword_mock.return_value = True
  672. try_to_execute_mock.return_value = {"exitstatus": 0, "log":"log0", "errormsg":"errormsg0"}
  673. bootstrap_obj.run()
  674. self.assertEqual(try_to_execute_mock.call_count, 10) # <- Adjust if changed
  675. self.assertTrue(createDoneFile_mock.called)
  676. self.assertEqual(bootstrap_obj.getStatus()["return_code"], 0)
  677. error_mock.reset_mock()
  678. write_mock.reset_mock()
  679. try_to_execute_mock.reset_mock()
  680. createDoneFile_mock.reset_mock()
  681. # Testing workflow when some action failed before copying password
  682. bootstrap_obj.copied_password_file = False
  683. hasPassword_mock.return_value = False
  684. try_to_execute_mock.side_effect = [{"exitstatus": 0, "log":"log0", "errormsg":"errormsg0"}, {"exitstatus": 1, "log":"log1", "errormsg":"errormsg1"}]
  685. bootstrap_obj.run()
  686. self.assertEqual(try_to_execute_mock.call_count, 2) # <- Adjust if changed
  687. self.assertTrue("ERROR" in error_mock.call_args[0][0])
  688. self.assertTrue("ERROR" in write_mock.call_args[0][0])
  689. self.assertTrue(createDoneFile_mock.called)
  690. self.assertEqual(bootstrap_obj.getStatus()["return_code"], 1)
  691. try_to_execute_mock.reset_mock()
  692. createDoneFile_mock.reset_mock()
  693. # Testing workflow when some action failed after copying password
  694. bootstrap_obj.copied_password_file = True
  695. hasPassword_mock.return_value = True
  696. try_to_execute_mock.side_effect = [{"exitstatus": 0, "log":"log0", "errormsg":"errormsg0"}, {"exitstatus": 42, "log":"log42", "errormsg":"errormsg42"}, {"exitstatus": 0, "log":"log0", "errormsg":"errormsg0"}]
  697. bootstrap_obj.run()
  698. self.assertEqual(try_to_execute_mock.call_count, 3) # <- Adjust if changed
  699. self.assertTrue(createDoneFile_mock.called)
  700. self.assertEqual(bootstrap_obj.getStatus()["return_code"], 42)
  701. error_mock.reset_mock()
  702. write_mock.reset_mock()
  703. try_to_execute_mock.reset_mock()
  704. createDoneFile_mock.reset_mock()
  705. # Testing workflow when some action failed after copying password and
  706. # removing password failed too
  707. bootstrap_obj.copied_password_file = True
  708. hasPassword_mock.return_value = True
  709. try_to_execute_mock.side_effect = [{"exitstatus": 0, "log":"log0", "errormsg":"errormsg0"}, {"exitstatus": 17, "log":"log17", "errormsg":"errormsg17"}, {"exitstatus": 19, "log":"log19", "errormsg":"errormsg19"}]
  710. bootstrap_obj.run()
  711. self.assertEqual(try_to_execute_mock.call_count, 3) # <- Adjust if changed
  712. self.assertTrue("ERROR" in write_mock.call_args_list[0][0][0])
  713. self.assertTrue("ERROR" in error_mock.call_args[0][0])
  714. self.assertTrue("WARNING" in write_mock.call_args_list[1][0][0])
  715. self.assertTrue("WARNING" in warn_mock.call_args[0][0])
  716. self.assertTrue(createDoneFile_mock.called)
  717. self.assertEqual(bootstrap_obj.getStatus()["return_code"], 17)
  718. @patch.object(BootstrapDefault, "createDoneFile")
  719. @patch.object(HostLog, "write")
  720. def test_interruptBootstrap(self, write_mock, createDoneFile_mock):
  721. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  722. "setupAgentFile", "ambariServer", "centos6",
  723. None, "8440", "root")
  724. bootstrap_obj = Bootstrap("hostname", shared_state)
  725. bootstrap_obj.interruptBootstrap()
  726. self.assertTrue(createDoneFile_mock.called)
  727. @patch("time.sleep")
  728. @patch("time.time")
  729. @patch("logging.warn")
  730. @patch("logging.info")
  731. @patch.object(BootstrapDefault, "start")
  732. @patch.object(BootstrapDefault, "interruptBootstrap")
  733. @patch.object(BootstrapDefault, "getStatus")
  734. def test_PBootstrap(self, getStatus_mock, interruptBootstrap_mock, start_mock,
  735. info_mock, warn_mock, time_mock, sleep_mock):
  736. shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
  737. "setupAgentFile", "ambariServer", "centos6",
  738. None, "8440", "root")
  739. n = 180
  740. time = 100500
  741. time_mock.return_value = time
  742. hosts = []
  743. for i in range(0, n):
  744. hosts.append("host" + str(i))
  745. # Testing normal case
  746. getStatus_mock.return_value = {"return_code": 0,
  747. "start_time": time + 999}
  748. pbootstrap_obj = PBootstrap(hosts, shared_state)
  749. pbootstrap_obj.run()
  750. self.assertEqual(start_mock.call_count, n)
  751. self.assertEqual(interruptBootstrap_mock.call_count, 0)
  752. start_mock.reset_mock()
  753. getStatus_mock.reset_mock()
  754. # Testing case of timeout
  755. def fake_return_code_generator():
  756. call_number = 0
  757. while True:
  758. call_number += 1
  759. if call_number % 5 != 0: # ~80% of hosts finish successfully
  760. yield 0
  761. else:
  762. yield None
  763. def fake_start_time_generator():
  764. while True:
  765. yield time - bootstrap.HOST_BOOTSTRAP_TIMEOUT - 1
  766. return_code_generator = fake_return_code_generator()
  767. start_time_generator = fake_start_time_generator()
  768. def status_get_item_mock(item):
  769. if item == "return_code":
  770. return return_code_generator.next()
  771. elif item == "start_time":
  772. return start_time_generator.next()
  773. dict_mock = MagicMock()
  774. dict_mock.__getitem__.side_effect = status_get_item_mock
  775. getStatus_mock.return_value = dict_mock
  776. pbootstrap_obj.run()
  777. self.assertEqual(start_mock.call_count, n)
  778. self.assertEqual(interruptBootstrap_mock.call_count, n / 5)