1
0

test_ssh.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. """
  2. :codeauthor: :email:`Daniel Wallace <dwallace@saltstack.com`
  3. """
  4. import os
  5. import re
  6. import shutil
  7. import tempfile
  8. import salt.config
  9. import salt.roster
  10. import salt.utils.files
  11. import salt.utils.path
  12. import salt.utils.thin
  13. import salt.utils.yaml
  14. from salt.client import ssh
  15. from tests.support.case import ShellCase
  16. from tests.support.helpers import slowTest
  17. from tests.support.mock import MagicMock, call, patch
  18. from tests.support.runtests import RUNTIME_VARS
  19. from tests.support.unit import TestCase, skipIf
  20. @skipIf(not salt.utils.path.which("ssh"), "No ssh binary found in path")
  21. class SSHPasswordTests(ShellCase):
  22. @slowTest
  23. def test_password_failure(self):
  24. """
  25. Check password failures when trying to deploy keys
  26. """
  27. opts = salt.config.client_config(self.get_config_file_path("master"))
  28. opts["list_hosts"] = False
  29. opts["argv"] = ["test.ping"]
  30. opts["selected_target_option"] = "glob"
  31. opts["tgt"] = "localhost"
  32. opts["arg"] = []
  33. roster = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "roster")
  34. handle_ssh_ret = [
  35. {
  36. "localhost": {
  37. "retcode": 255,
  38. "stderr": "Permission denied (publickey).\r\n",
  39. "stdout": "",
  40. }
  41. },
  42. ]
  43. expected = {"localhost": "Permission denied (publickey)"}
  44. display_output = MagicMock()
  45. with patch(
  46. "salt.roster.get_roster_file", MagicMock(return_value=roster)
  47. ), patch(
  48. "salt.client.ssh.SSH.handle_ssh", MagicMock(return_value=handle_ssh_ret)
  49. ), patch(
  50. "salt.client.ssh.SSH.key_deploy", MagicMock(return_value=expected)
  51. ), patch(
  52. "salt.output.display_output", display_output
  53. ):
  54. client = ssh.SSH(opts)
  55. ret = next(client.run_iter())
  56. with self.assertRaises(SystemExit):
  57. client.run()
  58. display_output.assert_called_once_with(expected, "nested", opts)
  59. self.assertIs(ret, handle_ssh_ret[0])
  60. @skipIf(not salt.utils.path.which("ssh"), "No ssh binary found in path")
  61. class SSHReturnEventTests(ShellCase):
  62. def test_not_missing_fun_calling_wfuncs(self):
  63. opts = salt.config.client_config(self.get_config_file_path("master"))
  64. opts["list_hosts"] = False
  65. opts["argv"] = ["state.show_highstate"]
  66. opts["selected_target_option"] = "glob"
  67. opts["tgt"] = "localhost"
  68. opts["arg"] = []
  69. roster = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "roster")
  70. handle_ssh_ret = [
  71. {"localhost": {}},
  72. ]
  73. expected = {"localhost": {}}
  74. display_output = MagicMock()
  75. with patch(
  76. "salt.roster.get_roster_file", MagicMock(return_value=roster)
  77. ), patch(
  78. "salt.client.ssh.SSH.handle_ssh", MagicMock(return_value=handle_ssh_ret)
  79. ), patch(
  80. "salt.client.ssh.SSH.key_deploy", MagicMock(return_value=expected)
  81. ), patch(
  82. "salt.output.display_output", display_output
  83. ):
  84. client = ssh.SSH(opts)
  85. client.event = MagicMock()
  86. ret = next(client.run_iter())
  87. assert "localhost" in ret
  88. assert "fun" in ret["localhost"]
  89. client.run()
  90. display_output.assert_called_once_with(expected, "nested", opts)
  91. self.assertIs(ret, handle_ssh_ret[0])
  92. assert len(client.event.fire_event.call_args_list) == 2
  93. assert "fun" in client.event.fire_event.call_args_list[0][0][0]
  94. assert "fun" in client.event.fire_event.call_args_list[1][0][0]
  95. class SSHRosterDefaults(TestCase):
  96. def setUp(self):
  97. self.roster = """
  98. localhost:
  99. host: 127.0.0.1
  100. port: 2827
  101. self:
  102. host: 0.0.0.0
  103. port: 42
  104. """
  105. def test_roster_defaults_flat(self):
  106. """
  107. Test Roster Defaults on the flat roster
  108. """
  109. tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  110. expected = {
  111. "self": {"host": "0.0.0.0", "user": "daniel", "port": 42},
  112. "localhost": {"host": "127.0.0.1", "user": "daniel", "port": 2827},
  113. }
  114. try:
  115. root_dir = os.path.join(tempdir, "foo", "bar")
  116. os.makedirs(root_dir)
  117. fpath = os.path.join(root_dir, "config")
  118. with salt.utils.files.fopen(fpath, "w") as fp_:
  119. fp_.write(
  120. """
  121. roster_defaults:
  122. user: daniel
  123. """
  124. )
  125. opts = salt.config.master_config(fpath)
  126. with patch(
  127. "salt.roster.get_roster_file", MagicMock(return_value=self.roster)
  128. ):
  129. with patch(
  130. "salt.template.compile_template",
  131. MagicMock(return_value=salt.utils.yaml.safe_load(self.roster)),
  132. ):
  133. roster = salt.roster.Roster(opts=opts)
  134. self.assertEqual(roster.targets("*", "glob"), expected)
  135. finally:
  136. if os.path.isdir(tempdir):
  137. shutil.rmtree(tempdir)
  138. class SSHSingleTests(TestCase):
  139. def setUp(self):
  140. self.tmp_cachedir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  141. self.argv = [
  142. "ssh.set_auth_key",
  143. "root",
  144. "hobn+amNAXSBTiOXEqlBjGB...rsa root@master",
  145. ]
  146. self.opts = {
  147. "argv": self.argv,
  148. "__role": "master",
  149. "cachedir": self.tmp_cachedir,
  150. "extension_modules": os.path.join(self.tmp_cachedir, "extmods"),
  151. }
  152. self.target = {
  153. "passwd": "abc123",
  154. "ssh_options": None,
  155. "sudo": False,
  156. "identities_only": False,
  157. "host": "login1",
  158. "user": "root",
  159. "timeout": 65,
  160. "remote_port_forwards": None,
  161. "sudo_user": "",
  162. "port": "22",
  163. "priv": "/etc/salt/pki/master/ssh/salt-ssh.rsa",
  164. }
  165. def test_single_opts(self):
  166. """ Sanity check for ssh.Single options
  167. """
  168. single = ssh.Single(
  169. self.opts,
  170. self.opts["argv"],
  171. "localhost",
  172. mods={},
  173. fsclient=None,
  174. thin=salt.utils.thin.thin_path(self.opts["cachedir"]),
  175. mine=False,
  176. **self.target
  177. )
  178. self.assertEqual(single.shell._ssh_opts(), "")
  179. self.assertEqual(
  180. single.shell._cmd_str("date +%s"),
  181. "ssh login1 "
  182. "-o KbdInteractiveAuthentication=no -o "
  183. "PasswordAuthentication=yes -o ConnectTimeout=65 -o Port=22 "
  184. "-o IdentityFile=/etc/salt/pki/master/ssh/salt-ssh.rsa "
  185. "-o User=root date +%s",
  186. )
  187. def test_run_with_pre_flight(self):
  188. """
  189. test Single.run() when ssh_pre_flight is set
  190. and script successfully runs
  191. """
  192. target = self.target.copy()
  193. target["ssh_pre_flight"] = os.path.join(RUNTIME_VARS.TMP, "script.sh")
  194. single = ssh.Single(
  195. self.opts,
  196. self.opts["argv"],
  197. "localhost",
  198. mods={},
  199. fsclient=None,
  200. thin=salt.utils.thin.thin_path(self.opts["cachedir"]),
  201. mine=False,
  202. **target
  203. )
  204. cmd_ret = ("Success", "", 0)
  205. mock_flight = MagicMock(return_value=cmd_ret)
  206. mock_cmd = MagicMock(return_value=cmd_ret)
  207. patch_flight = patch("salt.client.ssh.Single.run_ssh_pre_flight", mock_flight)
  208. patch_cmd = patch("salt.client.ssh.Single.cmd_block", mock_cmd)
  209. patch_exec_cmd = patch(
  210. "salt.client.ssh.shell.Shell.exec_cmd", return_value=("", "", 1)
  211. )
  212. patch_os = patch("os.path.exists", side_effect=[True])
  213. with patch_os, patch_flight, patch_cmd, patch_exec_cmd:
  214. ret = single.run()
  215. mock_cmd.assert_called()
  216. mock_flight.assert_called()
  217. assert ret == cmd_ret
  218. def test_run_with_pre_flight_stderr(self):
  219. """
  220. test Single.run() when ssh_pre_flight is set
  221. and script errors when run
  222. """
  223. target = self.target.copy()
  224. target["ssh_pre_flight"] = os.path.join(RUNTIME_VARS.TMP, "script.sh")
  225. single = ssh.Single(
  226. self.opts,
  227. self.opts["argv"],
  228. "localhost",
  229. mods={},
  230. fsclient=None,
  231. thin=salt.utils.thin.thin_path(self.opts["cachedir"]),
  232. mine=False,
  233. **target
  234. )
  235. cmd_ret = ("", "Error running script", 1)
  236. mock_flight = MagicMock(return_value=cmd_ret)
  237. mock_cmd = MagicMock(return_value=cmd_ret)
  238. patch_flight = patch("salt.client.ssh.Single.run_ssh_pre_flight", mock_flight)
  239. patch_cmd = patch("salt.client.ssh.Single.cmd_block", mock_cmd)
  240. patch_exec_cmd = patch(
  241. "salt.client.ssh.shell.Shell.exec_cmd", return_value=("", "", 1)
  242. )
  243. patch_os = patch("os.path.exists", side_effect=[True])
  244. with patch_os, patch_flight, patch_cmd, patch_exec_cmd:
  245. ret = single.run()
  246. mock_cmd.assert_not_called()
  247. mock_flight.assert_called()
  248. assert ret == cmd_ret
  249. def test_run_with_pre_flight_script_doesnot_exist(self):
  250. """
  251. test Single.run() when ssh_pre_flight is set
  252. and the script does not exist
  253. """
  254. target = self.target.copy()
  255. target["ssh_pre_flight"] = os.path.join(RUNTIME_VARS.TMP, "script.sh")
  256. single = ssh.Single(
  257. self.opts,
  258. self.opts["argv"],
  259. "localhost",
  260. mods={},
  261. fsclient=None,
  262. thin=salt.utils.thin.thin_path(self.opts["cachedir"]),
  263. mine=False,
  264. **target
  265. )
  266. cmd_ret = ("Success", "", 0)
  267. mock_flight = MagicMock(return_value=cmd_ret)
  268. mock_cmd = MagicMock(return_value=cmd_ret)
  269. patch_flight = patch("salt.client.ssh.Single.run_ssh_pre_flight", mock_flight)
  270. patch_cmd = patch("salt.client.ssh.Single.cmd_block", mock_cmd)
  271. patch_exec_cmd = patch(
  272. "salt.client.ssh.shell.Shell.exec_cmd", return_value=("", "", 1)
  273. )
  274. patch_os = patch("os.path.exists", side_effect=[False])
  275. with patch_os, patch_flight, patch_cmd, patch_exec_cmd:
  276. ret = single.run()
  277. mock_cmd.assert_called()
  278. mock_flight.assert_not_called()
  279. assert ret == cmd_ret
  280. def test_run_with_pre_flight_thin_dir_exists(self):
  281. """
  282. test Single.run() when ssh_pre_flight is set
  283. and thin_dir already exists
  284. """
  285. target = self.target.copy()
  286. target["ssh_pre_flight"] = os.path.join(RUNTIME_VARS.TMP, "script.sh")
  287. single = ssh.Single(
  288. self.opts,
  289. self.opts["argv"],
  290. "localhost",
  291. mods={},
  292. fsclient=None,
  293. thin=salt.utils.thin.thin_path(self.opts["cachedir"]),
  294. mine=False,
  295. **target
  296. )
  297. cmd_ret = ("", "", 0)
  298. mock_flight = MagicMock(return_value=cmd_ret)
  299. mock_cmd = MagicMock(return_value=cmd_ret)
  300. patch_flight = patch("salt.client.ssh.Single.run_ssh_pre_flight", mock_flight)
  301. patch_cmd = patch("salt.client.ssh.shell.Shell.exec_cmd", mock_cmd)
  302. patch_cmd_block = patch("salt.client.ssh.Single.cmd_block", mock_cmd)
  303. patch_os = patch("os.path.exists", return_value=True)
  304. with patch_os, patch_flight, patch_cmd, patch_cmd_block:
  305. ret = single.run()
  306. mock_cmd.assert_called()
  307. mock_flight.assert_not_called()
  308. assert ret == cmd_ret
  309. def test_execute_script(self):
  310. """
  311. test Single.execute_script()
  312. """
  313. single = ssh.Single(
  314. self.opts,
  315. self.opts["argv"],
  316. "localhost",
  317. mods={},
  318. fsclient=None,
  319. thin=salt.utils.thin.thin_path(self.opts["cachedir"]),
  320. mine=False,
  321. winrm=False,
  322. **self.target
  323. )
  324. exp_ret = ("Success", "", 0)
  325. mock_cmd = MagicMock(return_value=exp_ret)
  326. patch_cmd = patch("salt.client.ssh.shell.Shell.exec_cmd", mock_cmd)
  327. script = os.path.join(RUNTIME_VARS.TMP, "script.sh")
  328. with patch_cmd:
  329. ret = single.execute_script(script=script)
  330. assert ret == exp_ret
  331. assert mock_cmd.call_count == 2
  332. assert [
  333. call("/bin/sh '{}'".format(script)),
  334. call("rm '{}'".format(script)),
  335. ] == mock_cmd.call_args_list
  336. def test_shim_cmd(self):
  337. """
  338. test Single.shim_cmd()
  339. """
  340. single = ssh.Single(
  341. self.opts,
  342. self.opts["argv"],
  343. "localhost",
  344. mods={},
  345. fsclient=None,
  346. thin=salt.utils.thin.thin_path(self.opts["cachedir"]),
  347. mine=False,
  348. winrm=False,
  349. tty=True,
  350. **self.target
  351. )
  352. exp_ret = ("Success", "", 0)
  353. mock_cmd = MagicMock(return_value=exp_ret)
  354. patch_cmd = patch("salt.client.ssh.shell.Shell.exec_cmd", mock_cmd)
  355. patch_send = patch("salt.client.ssh.shell.Shell.send", return_value=("", "", 0))
  356. patch_rand = patch("os.urandom", return_value=b"5\xd9l\xca\xc2\xff")
  357. with patch_cmd, patch_rand, patch_send:
  358. ret = single.shim_cmd(cmd_str="echo test")
  359. assert ret == exp_ret
  360. assert [
  361. call("/bin/sh '.35d96ccac2ff.py'"),
  362. call("rm '.35d96ccac2ff.py'"),
  363. ] == mock_cmd.call_args_list
  364. def test_run_ssh_pre_flight(self):
  365. """
  366. test Single.run_ssh_pre_flight
  367. """
  368. target = self.target.copy()
  369. target["ssh_pre_flight"] = os.path.join(RUNTIME_VARS.TMP, "script.sh")
  370. single = ssh.Single(
  371. self.opts,
  372. self.opts["argv"],
  373. "localhost",
  374. mods={},
  375. fsclient=None,
  376. thin=salt.utils.thin.thin_path(self.opts["cachedir"]),
  377. mine=False,
  378. winrm=False,
  379. tty=True,
  380. **target
  381. )
  382. exp_ret = ("Success", "", 0)
  383. mock_cmd = MagicMock(return_value=exp_ret)
  384. patch_cmd = patch("salt.client.ssh.shell.Shell.exec_cmd", mock_cmd)
  385. patch_send = patch("salt.client.ssh.shell.Shell.send", return_value=exp_ret)
  386. exp_tmp = os.path.join(
  387. tempfile.gettempdir(), os.path.basename(target["ssh_pre_flight"])
  388. )
  389. with patch_cmd, patch_send:
  390. ret = single.run_ssh_pre_flight()
  391. assert ret == exp_ret
  392. assert [
  393. call("/bin/sh '{}'".format(exp_tmp)),
  394. call("rm '{}'".format(exp_tmp)),
  395. ] == mock_cmd.call_args_list
  396. @skipIf(salt.utils.platform.is_windows(), "SSH_PY_SHIM not set on windows")
  397. def test_cmd_run_set_path(self):
  398. """
  399. test when set_path is set
  400. """
  401. target = self.target
  402. target["set_path"] = "$PATH:/tmp/path/"
  403. single = ssh.Single(
  404. self.opts,
  405. self.opts["argv"],
  406. "localhost",
  407. mods={},
  408. fsclient=None,
  409. thin=salt.utils.thin.thin_path(self.opts["cachedir"]),
  410. mine=False,
  411. **self.target
  412. )
  413. ret = single._cmd_str()
  414. assert re.search("\\" + target["set_path"], ret)
  415. @skipIf(salt.utils.platform.is_windows(), "SSH_PY_SHIM not set on windows")
  416. def test_cmd_run_not_set_path(self):
  417. """
  418. test when set_path is not set
  419. """
  420. target = self.target
  421. single = ssh.Single(
  422. self.opts,
  423. self.opts["argv"],
  424. "localhost",
  425. mods={},
  426. fsclient=None,
  427. thin=salt.utils.thin.thin_path(self.opts["cachedir"]),
  428. mine=False,
  429. **self.target
  430. )
  431. ret = single._cmd_str()
  432. assert re.search('SET_PATH=""', ret)
  433. @skipIf(not salt.utils.path.which("ssh"), "No ssh binary found in path")
  434. class SSHTests(ShellCase):
  435. def setUp(self):
  436. self.roster = """
  437. localhost:
  438. host: 127.0.0.1
  439. port: 2827
  440. """
  441. self.opts = salt.config.client_config(self.get_config_file_path("master"))
  442. self.opts["selected_target_option"] = "glob"
  443. def test_expand_target_ip_address(self):
  444. """
  445. test expand_target when target is root@<ip address>
  446. """
  447. host = "127.0.0.1"
  448. user = "test-user@"
  449. opts = self.opts
  450. opts["tgt"] = user + host
  451. with patch(
  452. "salt.utils.network.is_reachable_host", MagicMock(return_value=False)
  453. ):
  454. client = ssh.SSH(opts)
  455. assert opts["tgt"] == user + host
  456. with patch(
  457. "salt.roster.get_roster_file", MagicMock(return_value="/etc/salt/roster")
  458. ), patch(
  459. "salt.client.ssh.compile_template",
  460. MagicMock(return_value=salt.utils.yaml.safe_load(self.roster)),
  461. ):
  462. client._expand_target()
  463. assert opts["tgt"] == host
  464. def test_expand_target_dns(self):
  465. """
  466. test expand_target when target is root@<dns>
  467. """
  468. host = "localhost"
  469. user = "test-user@"
  470. opts = self.opts
  471. opts["tgt"] = user + host
  472. with patch(
  473. "salt.utils.network.is_reachable_host", MagicMock(return_value=False)
  474. ):
  475. client = ssh.SSH(opts)
  476. assert opts["tgt"] == user + host
  477. with patch(
  478. "salt.roster.get_roster_file", MagicMock(return_value="/etc/salt/roster")
  479. ), patch(
  480. "salt.client.ssh.compile_template",
  481. MagicMock(return_value=salt.utils.yaml.safe_load(self.roster)),
  482. ):
  483. client._expand_target()
  484. assert opts["tgt"] == host
  485. def test_expand_target_no_user(self):
  486. """
  487. test expand_target when no user defined
  488. """
  489. host = "127.0.0.1"
  490. opts = self.opts
  491. opts["tgt"] = host
  492. with patch(
  493. "salt.utils.network.is_reachable_host", MagicMock(return_value=False)
  494. ):
  495. client = ssh.SSH(opts)
  496. assert opts["tgt"] == host
  497. with patch(
  498. "salt.roster.get_roster_file", MagicMock(return_value="/etc/salt/roster")
  499. ), patch(
  500. "salt.client.ssh.compile_template",
  501. MagicMock(return_value=salt.utils.yaml.safe_load(self.roster)),
  502. ):
  503. client._expand_target()
  504. assert opts["tgt"] == host
  505. def test_update_targets_ip_address(self):
  506. """
  507. test update_targets when host is ip address
  508. """
  509. host = "127.0.0.1"
  510. user = "test-user@"
  511. opts = self.opts
  512. opts["tgt"] = user + host
  513. with patch(
  514. "salt.utils.network.is_reachable_host", MagicMock(return_value=False)
  515. ):
  516. client = ssh.SSH(opts)
  517. assert opts["tgt"] == user + host
  518. client._update_targets()
  519. assert opts["tgt"] == host
  520. assert client.targets[host]["user"] == user.split("@")[0]
  521. def test_update_targets_dns(self):
  522. """
  523. test update_targets when host is dns
  524. """
  525. host = "localhost"
  526. user = "test-user@"
  527. opts = self.opts
  528. opts["tgt"] = user + host
  529. with patch(
  530. "salt.utils.network.is_reachable_host", MagicMock(return_value=False)
  531. ):
  532. client = ssh.SSH(opts)
  533. assert opts["tgt"] == user + host
  534. client._update_targets()
  535. assert opts["tgt"] == host
  536. assert client.targets[host]["user"] == user.split("@")[0]
  537. def test_update_targets_no_user(self):
  538. """
  539. test update_targets when no user defined
  540. """
  541. host = "127.0.0.1"
  542. opts = self.opts
  543. opts["tgt"] = host
  544. with patch(
  545. "salt.utils.network.is_reachable_host", MagicMock(return_value=False)
  546. ):
  547. client = ssh.SSH(opts)
  548. assert opts["tgt"] == host
  549. client._update_targets()
  550. assert opts["tgt"] == host
  551. def test_update_expand_target_dns(self):
  552. """
  553. test update_targets and expand_target when host is dns
  554. """
  555. host = "localhost"
  556. user = "test-user@"
  557. opts = self.opts
  558. opts["tgt"] = user + host
  559. with patch(
  560. "salt.utils.network.is_reachable_host", MagicMock(return_value=False)
  561. ):
  562. client = ssh.SSH(opts)
  563. assert opts["tgt"] == user + host
  564. with patch(
  565. "salt.roster.get_roster_file", MagicMock(return_value="/etc/salt/roster")
  566. ), patch(
  567. "salt.client.ssh.compile_template",
  568. MagicMock(return_value=salt.utils.yaml.safe_load(self.roster)),
  569. ):
  570. client._expand_target()
  571. client._update_targets()
  572. assert opts["tgt"] == host
  573. assert client.targets[host]["user"] == user.split("@")[0]
  574. def test_parse_tgt(self):
  575. """
  576. test parse_tgt when user and host set on
  577. the ssh cli tgt
  578. """
  579. host = "localhost"
  580. user = "test-user@"
  581. opts = self.opts
  582. opts["tgt"] = user + host
  583. with patch(
  584. "salt.utils.network.is_reachable_host", MagicMock(return_value=False)
  585. ):
  586. assert not self.opts.get("ssh_cli_tgt")
  587. client = ssh.SSH(opts)
  588. assert client.parse_tgt["hostname"] == host
  589. assert client.parse_tgt["user"] == user.split("@")[0]
  590. assert self.opts.get("ssh_cli_tgt") == user + host
  591. def test_parse_tgt_no_user(self):
  592. """
  593. test parse_tgt when only the host set on
  594. the ssh cli tgt
  595. """
  596. host = "localhost"
  597. opts = self.opts
  598. opts["ssh_user"] = "ssh-usr"
  599. opts["tgt"] = host
  600. with patch(
  601. "salt.utils.network.is_reachable_host", MagicMock(return_value=False)
  602. ):
  603. assert not self.opts.get("ssh_cli_tgt")
  604. client = ssh.SSH(opts)
  605. assert client.parse_tgt["hostname"] == host
  606. assert client.parse_tgt["user"] == opts["ssh_user"]
  607. assert self.opts.get("ssh_cli_tgt") == host