test_smb.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. # -*- coding: utf-8 -*-
  2. """
  3. Test utility methods that communicate with SMB shares.
  4. """
  5. from __future__ import absolute_import
  6. import getpass
  7. import logging
  8. import os
  9. import signal
  10. import subprocess
  11. import tempfile
  12. import time
  13. import salt.utils.files
  14. import salt.utils.path
  15. import salt.utils.smb
  16. from tests.support.case import TestCase
  17. from tests.support.unit import skipIf
  18. log = logging.getLogger(__name__)
  19. CONFIG = (
  20. "[global]\n"
  21. "realm = saltstack.com\n"
  22. "interfaces = lo 127.0.0.0/8\n"
  23. "smb ports = 1445\n"
  24. "log level = 2\n"
  25. "map to guest = Bad User\n"
  26. "enable core files = no\n"
  27. "passdb backend = smbpasswd\n"
  28. "smb passwd file = {passwdb}\n"
  29. "lock directory = {samba_dir}\n"
  30. "state directory = {samba_dir}\n"
  31. "cache directory = {samba_dir}\n"
  32. "pid directory = {samba_dir}\n"
  33. "private dir = {samba_dir}\n"
  34. "ncalrpc dir = {samba_dir}\n"
  35. "socket options = IPTOS_LOWDELAY TCP_NODELAY\n"
  36. "min receivefile size = 0\n"
  37. "write cache size = 0\n"
  38. "client ntlmv2 auth = no\n"
  39. "client min protocol = SMB3_11\n"
  40. "client plaintext auth = no\n"
  41. "\n"
  42. "[public]\n"
  43. "path = {public_dir}\n"
  44. "read only = no\n"
  45. "guest ok = no\n"
  46. "writeable = yes\n"
  47. "force user = {user}\n"
  48. )
  49. TBE = (
  50. "{}:0:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:AC8E657F8"
  51. "3DF82BEEA5D43BDAF7800CC:[U ]:LCT-507C14C7:"
  52. )
  53. def which_smbd():
  54. """
  55. Find the smbd executable and cache the result if it exits.
  56. """
  57. if hasattr(which_smbd, "cached_result"):
  58. return which_smbd.cached_result
  59. smbd = salt.utils.path.which("smbd")
  60. if smbd:
  61. which_smbd.cached_result = smbd
  62. return smbd
  63. @skipIf(not which_smbd(), reason="smbd binary not found")
  64. @skipIf(
  65. not salt.utils.smb.HAS_SMBPROTOCOL, '"smbprotocol" needs to be installed.',
  66. )
  67. class TestSmb(TestCase):
  68. _smbd = None
  69. @staticmethod
  70. def check_pid(pid):
  71. try:
  72. os.kill(pid, 0)
  73. except OSError:
  74. return False
  75. else:
  76. return True
  77. @classmethod
  78. def setUpClass(cls):
  79. tmpdir = tempfile.mkdtemp()
  80. cls.samba_dir = os.path.join(tmpdir, "samba")
  81. cls.public_dir = os.path.join(tmpdir, "public")
  82. os.makedirs(cls.samba_dir)
  83. os.makedirs(cls.public_dir)
  84. os.chmod(cls.samba_dir, 0o775)
  85. os.chmod(cls.public_dir, 0o775)
  86. passwdb = os.path.join(tmpdir, "passwdb")
  87. cls.username = getpass.getuser()
  88. with salt.utils.files.fopen(passwdb, "w") as fp:
  89. fp.write(TBE.format(cls.username))
  90. samba_conf = os.path.join(tmpdir, "smb.conf")
  91. with salt.utils.files.fopen(samba_conf, "w") as fp:
  92. fp.write(
  93. CONFIG.format(
  94. samba_dir=cls.samba_dir,
  95. public_dir=cls.public_dir,
  96. passwdb=passwdb,
  97. user=cls.username,
  98. )
  99. )
  100. cls._smbd = subprocess.Popen(
  101. "{0} -FS -P0 -s {1}".format(which_smbd(), samba_conf), shell=True
  102. )
  103. time.sleep(1)
  104. pidfile = os.path.join(cls.samba_dir, "smbd.pid")
  105. with salt.utils.files.fopen(pidfile, "r") as fp:
  106. cls._pid = int(fp.read().strip())
  107. if not cls.check_pid(cls._pid):
  108. raise Exception("Unable to locate smbd's pid file")
  109. @classmethod
  110. def tearDownClass(cls):
  111. log.warning("teardown")
  112. os.kill(cls._pid, signal.SIGTERM)
  113. def test_write_file(self):
  114. """
  115. Transfer a file over SMB
  116. """
  117. name = "test_write_file.txt"
  118. content = "write test file content"
  119. share_path = os.path.join(self.public_dir, name)
  120. assert not os.path.exists(share_path)
  121. local_path = tempfile.mktemp()
  122. with salt.utils.files.fopen(local_path, "w") as fp:
  123. fp.write(content)
  124. conn = salt.utils.smb.get_conn("127.0.0.1", self.username, "foo", port=1445)
  125. salt.utils.smb.put_file(local_path, name, "public", conn=conn)
  126. conn.close()
  127. assert os.path.exists(share_path)
  128. with salt.utils.files.fopen(share_path, "r") as fp:
  129. result = fp.read()
  130. assert result == content
  131. def test_write_str(self):
  132. """
  133. Write a string to a file over SMB
  134. """
  135. name = "test_write_str.txt"
  136. content = "write test file content"
  137. share_path = os.path.join(self.public_dir, name)
  138. assert not os.path.exists(share_path)
  139. conn = salt.utils.smb.get_conn("127.0.0.1", self.username, "foo", port=1445)
  140. salt.utils.smb.put_str(content, name, "public", conn=conn)
  141. conn.close()
  142. assert os.path.exists(share_path)
  143. with salt.utils.files.fopen(share_path, "r") as fp:
  144. result = fp.read()
  145. assert result == content
  146. def test_delete_file(self):
  147. """
  148. Validate deletion of files over SMB
  149. """
  150. name = "test_delete_file.txt"
  151. content = "read test file content"
  152. share_path = os.path.join(self.public_dir, name)
  153. with salt.utils.files.fopen(share_path, "w") as fp:
  154. fp.write(content)
  155. assert os.path.exists(share_path)
  156. conn = salt.utils.smb.get_conn("127.0.0.1", self.username, "foo", port=1445)
  157. salt.utils.smb.delete_file(name, "public", conn=conn)
  158. conn.close()
  159. assert not os.path.exists(share_path)
  160. def test_mkdirs(self):
  161. """
  162. Create directories over SMB
  163. """
  164. dir_name = "mkdirs/test"
  165. share_path = os.path.join(self.public_dir, dir_name)
  166. assert not os.path.exists(share_path)
  167. conn = salt.utils.smb.get_conn("127.0.0.1", self.username, "foo", port=1445)
  168. salt.utils.smb.mkdirs(dir_name, "public", conn=conn)
  169. conn.close()
  170. assert os.path.exists(share_path)
  171. def test_delete_dirs(self):
  172. """
  173. Validate deletion of directoreies over SMB
  174. """
  175. dir_name = "deldirs"
  176. subdir_name = "deldirs/test"
  177. local_path = os.path.join(self.public_dir, subdir_name)
  178. os.makedirs(local_path)
  179. assert os.path.exists(local_path)
  180. conn = salt.utils.smb.get_conn("127.0.0.1", self.username, "foo", port=1445)
  181. salt.utils.smb.delete_directory(subdir_name, "public", conn=conn)
  182. conn.close()
  183. conn = salt.utils.smb.get_conn("127.0.0.1", self.username, "foo", port=1445)
  184. salt.utils.smb.delete_directory(dir_name, "public", conn=conn)
  185. conn.close()
  186. assert not os.path.exists(local_path)
  187. assert not os.path.exists(os.path.join(self.public_dir, dir_name))
  188. def test_connection(self):
  189. """
  190. Validate creation of an SMB connection
  191. """
  192. conn = salt.utils.smb.get_conn("127.0.0.1", self.username, "foo", port=1445)
  193. conn.close()