# -*- coding: utf-8 -*- ''' Test utility methods that communicate with SMB shares. ''' from __future__ import absolute_import import getpass import logging import os import signal import subprocess import tempfile import time import salt.utils.files import salt.utils.path import salt.utils.smb from tests.support.unit import skipIf from tests.support.case import TestCase log = logging.getLogger(__name__) CONFIG = ( '[global]\n' 'realm = saltstack.com\n' 'interfaces = lo 127.0.0.0/8\n' 'smb ports = 1445\n' 'log level = 2\n' 'map to guest = Bad User\n' 'enable core files = no\n' 'passdb backend = smbpasswd\n' 'smb passwd file = {passwdb}\n' 'lock directory = {samba_dir}\n' 'state directory = {samba_dir}\n' 'cache directory = {samba_dir}\n' 'pid directory = {samba_dir}\n' 'private dir = {samba_dir}\n' 'ncalrpc dir = {samba_dir}\n' 'socket options = IPTOS_LOWDELAY TCP_NODELAY\n' 'min receivefile size = 0\n' 'write cache size = 0\n' 'client ntlmv2 auth = no\n' 'client min protocol = SMB3_11\n' 'client plaintext auth = no\n' '\n' '[public]\n' 'path = {public_dir}\n' 'read only = no\n' 'guest ok = no\n' 'writeable = yes\n' 'force user = {user}\n' ) TBE = ( '{}:0:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:AC8E657F8' '3DF82BEEA5D43BDAF7800CC:[U ]:LCT-507C14C7:' ) def which_smbd(): ''' Find the smbd executable and cache the result if it exits. ''' if hasattr(which_smbd, 'cached_result'): return which_smbd.cached_result smbd = salt.utils.path.which('smbd') if smbd: which_smbd.cached_result = smbd return smbd @skipIf(not which_smbd(), 'Skip when no smbd binary found') class TestSmb(TestCase): _smbd = None @staticmethod def check_pid(pid): try: os.kill(pid, 0) except OSError: return False else: return True @classmethod def setUpClass(cls): tmpdir = tempfile.mkdtemp() cls.samba_dir = os.path.join(tmpdir, 'samba') cls.public_dir = os.path.join(tmpdir, 'public') os.makedirs(cls.samba_dir) os.makedirs(cls.public_dir) os.chmod(cls.samba_dir, 0o775) os.chmod(cls.public_dir, 0o775) passwdb = os.path.join(tmpdir, 'passwdb') cls.username = getpass.getuser() with salt.utils.files.fopen(passwdb, 'w') as fp: fp.write(TBE.format(cls.username)) samba_conf = os.path.join(tmpdir, 'smb.conf') with salt.utils.files.fopen(samba_conf, 'w') as fp: fp.write( CONFIG.format( samba_dir=cls.samba_dir, public_dir=cls.public_dir, passwdb=passwdb, user=cls.username, ) ) cls._smbd = subprocess.Popen( '{0} -FS -P0 -s {1}'.format(which_smbd(), samba_conf), shell=True ) time.sleep(1) pidfile = os.path.join(cls.samba_dir, 'smbd.pid') with salt.utils.files.fopen(pidfile, 'r') as fp: cls._pid = int(fp.read().strip()) if not cls.check_pid(cls._pid): raise Exception('Unable to locate smbd\'s pid file') @classmethod def tearDownClass(cls): log.warning('teardown') os.kill(cls._pid, signal.SIGTERM) def test_write_file(self): ''' Transfer a file over SMB ''' name = 'test_write_file.txt' content = 'write test file content' share_path = os.path.join(self.public_dir, name) assert not os.path.exists(share_path) local_path = tempfile.mktemp() with salt.utils.files.fopen(local_path, 'w') as fp: fp.write(content) conn = salt.utils.smb.get_conn('127.0.0.1', self.username, 'foo', port=1445) salt.utils.smb.put_file(local_path, name, 'public', conn=conn) conn.close() assert os.path.exists(share_path) with salt.utils.files.fopen(share_path, 'r') as fp: result = fp.read() assert result == content def test_write_str(self): ''' Write a string to a file over SMB ''' name = 'test_write_str.txt' content = 'write test file content' share_path = os.path.join(self.public_dir, name) assert not os.path.exists(share_path) conn = salt.utils.smb.get_conn('127.0.0.1', self.username, 'foo', port=1445) salt.utils.smb.put_str(content, name, 'public', conn=conn) conn.close() assert os.path.exists(share_path) with salt.utils.files.fopen(share_path, 'r') as fp: result = fp.read() assert result == content def test_delete_file(self): ''' Validate deletion of files over SMB ''' name = 'test_delete_file.txt' content = 'read test file content' share_path = os.path.join(self.public_dir, name) with salt.utils.files.fopen(share_path, 'w') as fp: fp.write(content) assert os.path.exists(share_path) conn = salt.utils.smb.get_conn('127.0.0.1', self.username, 'foo', port=1445) salt.utils.smb.delete_file(name, 'public', conn=conn) conn.close() assert not os.path.exists(share_path) def test_mkdirs(self): ''' Create directories over SMB ''' dir_name = 'mkdirs/test' share_path = os.path.join(self.public_dir, dir_name) assert not os.path.exists(share_path) conn = salt.utils.smb.get_conn('127.0.0.1', self.username, 'foo', port=1445) salt.utils.smb.mkdirs(dir_name, 'public', conn=conn) conn.close() assert os.path.exists(share_path) def test_delete_dirs(self): ''' Validate deletion of directoreies over SMB ''' dir_name = 'deldirs' subdir_name = 'deldirs/test' local_path = os.path.join(self.public_dir, subdir_name) os.makedirs(local_path) assert os.path.exists(local_path) conn = salt.utils.smb.get_conn('127.0.0.1', self.username, 'foo', port=1445) salt.utils.smb.delete_directory(subdir_name, 'public', conn=conn) conn.close() conn = salt.utils.smb.get_conn('127.0.0.1', self.username, 'foo', port=1445) salt.utils.smb.delete_directory(dir_name, 'public', conn=conn) conn.close() assert not os.path.exists(local_path) assert not os.path.exists(os.path.join(self.public_dir, dir_name)) def test_connection(self): ''' Validate creation of an SMB connection ''' conn = salt.utils.smb.get_conn('127.0.0.1', self.username, 'foo', port=1445) conn.close()