Skip to content

Commit

Permalink
Merge branch 'samba-in-kubernetes:main' into shadow-copy
Browse files Browse the repository at this point in the history
  • Loading branch information
Shwetha-Acharya authored Aug 6, 2024
2 parents 0932513 + 5426d82 commit 510167d
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 82 deletions.
4 changes: 3 additions & 1 deletion test-info.yml.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ shares:
path: /mnt/share/export1-cephfs-vfs
backend:
# If present override default backend filesystem
name: cephfs.vfs
name: cephfs
# If present list the variant of the backend filesystem
variant: vfs
# If present, use these credentials to perform the
# tests for this share
users:
Expand Down
4 changes: 4 additions & 0 deletions testcases/smbtorture/selftest/flapping.cephfs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ samba3.smb2.timestamps.time_t_10000000000
samba3.smb2.timestamps.time_t_-1
samba3.smb2.timestamps.time_t_-2
samba3.smb2.timestamps.time_t_1968

# https://github.com/samba-in-kubernetes/sit-environment/pull/109
# Note: CephFS(vfs) successfully completes smb2.session.reauth4.
samba3.smb2.session.reauth4
18 changes: 18 additions & 0 deletions testcases/smbtorture/selftest/flapping.cephfs-vfs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# https://github.com/samba-in-kubernetes/sit-test-cases/issues/35
^samba3.smb2.rw.invalid

# This is a known fail for samba4 env. We explicitly mark env as
# samba3 which means we don't ever match with this knownfail.
^samba3.smb2.create.quota-fake-file

# Ignore due to lack of proper multichannel setup.
^samba3.smb2.session.bind2
^samba3.smb2.session.two_logoff

# https://github.com/samba-in-kubernetes/sit-test-cases/issues/71
# https://tracker.ceph.com/issues/65043
samba3.smb2.timestamps.time_t_15032385535
samba3.smb2.timestamps.time_t_10000000000
samba3.smb2.timestamps.time_t_-1
samba3.smb2.timestamps.time_t_-2
samba3.smb2.timestamps.time_t_1968
3 changes: 3 additions & 0 deletions testcases/smbtorture/selftest/flapping.xfs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
# Ignore due to lack of proper multichannel setup.
^samba3.smb2.session.bind2
^samba3.smb2.session.two_logoff

# https://github.com/samba-in-kubernetes/sit-environment/pull/109
samba3.smb2.session.reauth4
26 changes: 18 additions & 8 deletions testcases/smbtorture/test_smbtorture.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,26 @@

script_root = os.path.dirname(os.path.realpath(__file__))
smbtorture_exec = "/bin/smbtorture"
filter_subunit_exec = script_root + "/selftest/filter-subunit"
format_subunit_exec = script_root + "/selftest/format-subunit"
selftest_dir = script_root + "/selftest"
filter_subunit_exec = selftest_dir + "/filter-subunit"
format_subunit_exec = selftest_dir + "/format-subunit"
smbtorture_tests_file = script_root + "/smbtorture-tests-info.yml"

test_info_file = os.getenv("TEST_INFO_FILE")
test_info = testhelper.read_yaml(test_info_file)


def flapping_file(backend: str, variant: str = "") -> str:
file = "flapping." + backend
if variant and variant != "default":
file_variant = f"{file}-{variant}"
if os.path.exists(os.path.join(selftest_dir, file_variant)):
return file_variant
if os.path.exists(os.path.join(selftest_dir, file)):
return file
return ""


def smbtorture(share_name: str, test: str, tmp_output: Path) -> bool:
# build smbtorture command
mount_params = testhelper.get_mount_parameters(test_info, share_name)
Expand Down Expand Up @@ -50,13 +62,11 @@ def smbtorture(share_name: str, test: str, tmp_output: Path) -> bool:
flapping_list = ["flapping", "flapping.d"]
share = testhelper.get_share(test_info, share_name)
test_backend = share["backend"].get("name")
test_backend_variant = share["backend"].get("variant")
if test_backend is not None:
flapping_file = "flapping." + test_backend
flapping_file_path = os.path.join(
script_root, "selftest", flapping_file
)
if os.path.exists(flapping_file_path):
flapping_list.append(flapping_file)
file = flapping_file(test_backend, test_backend_variant)
if file is not None:
flapping_list.append(file)
for filter in flapping_list:
filter_subunit_cmd.append(
"--flapping=" + script_root + "/selftest/" + filter
Expand Down
171 changes: 103 additions & 68 deletions testhelper/smbclient.py
Original file line number Diff line number Diff line change
@@ -1,111 +1,146 @@
from smb.SMBConnection import SMBConnection # type: ignore
from smb import smb_structs, base # type: ignore
from smbprotocol.exceptions import SMBException # type: ignore
import smbclient # type: ignore
from smbprotocol.connection import Connection # type: ignore
import typing
import io
import uuid

rw_chunk_size = 1 << 21 # 2MB


class SMBClient:
"""Use pysmb to access the SMB server"""
"""Use smbprotocol python module to access the SMB server"""

def __init__(self, hostname: str, share: str, username: str, passwd: str):
def __init__(
self,
hostname: str,
share: str,
username: str,
passwd: str,
port: int = 445,
):
self.server = hostname
self.share = share
self.username = username
self.password = passwd
self.port = port
self.connection_cache: dict = {}
self.client_params = {
"username": username,
"password": passwd,
"connection_cache": self.connection_cache,
}
self.prepath = f"\\\\{self.server}\\{self.share}\\"
self.connected = False
self.connect()

def _path(self, path: str = "/") -> str:
path.replace("/", "\\")
return self.prepath + path

def connect(self) -> None:
if self.connected:
return
try:
self.ctx = SMBConnection(
self.username,
self.password,
"smbclient",
self.server,
use_ntlm_v2=True,
# Manually setup connection to avoid re-using guid through
# the global configuration
connection_key = f"{self.server.lower()}:{self.port}"
connection = Connection(uuid.uuid4(), self.server, self.port)
connection.connect()
self.connection_cache[connection_key] = connection
smbclient.register_session(
self.server, port=self.port, **self.client_params
)
self.ctx.connect(self.server)
self.connected = True
except base.SMBTimeout as error:
except SMBException as error:
raise IOError(f"failed to connect: {error}")

def disconnect(self) -> None:
self.connected = False
try:
self.ctx.close()
except base.SMBTimeout as error:
raise TimeoutError(f"disconnect: {error}")
smbclient.reset_connection_cache(
connection_cache=self.connection_cache
)

def _check_connected(self, action: str) -> None:
if not self.connected:
raise ConnectionError(f"{action}: server not connected")

def listdir(self, path: str = "/") -> typing.List[str]:
self._check_connected("listdir")
try:
dentries = self.ctx.listPath(self.share, path)
except smb_structs.OperationFailure as error:
raise IOError(f"failed to readdir: {error}")
except base.SMBTimeout as error:
raise TimeoutError(f"listdir: {error}")
except base.NotConnectedError as error:
raise ConnectionError(f"listdir: {error}")

return [dent.filename for dent in dentries]
filenames = smbclient.listdir(
self._path(path), **self.client_params
)
except SMBException as error:
raise IOError(f"listdir: {error}")
return filenames

def mkdir(self, dpath: str) -> None:
self._check_connected("mkdir")
if not self.connected:
raise ConnectionError("listdir: server not connected")
try:
self.ctx.createDirectory(self.share, dpath)
except smb_structs.OperationFailure as error:
raise IOError(f"failed to mkdir: {error}")
except base.SMBTimeout as error:
raise TimeoutError(f"mkdir: {error}")
except base.NotConnectedError as error:
raise ConnectionError(f"mkdir: {error}")
smbclient.mkdir(self._path(dpath), **self.client_params)
except SMBException as error:
raise IOError(f"mkdir: {error}")

def rmdir(self, dpath: str) -> None:
self._check_connected("rmdir")
try:
self.ctx.deleteDirectory(self.share, dpath)
except smb_structs.OperationFailure as error:
raise IOError(f"failed to rmdir: {error}")
except base.SMBTimeout as error:
raise TimeoutError(f"rmdir: {error}")
except base.NotConnectedError as error:
raise ConnectionError(f"rmdir: {error}")
smbclient.rmdir(self._path(dpath), **self.client_params)
except SMBException as error:
raise IOError(f"rmdir: {error}")

def unlink(self, fpath: str) -> None:
self._check_connected("unlink")
try:
self.ctx.deleteFiles(self.share, fpath)
except smb_structs.OperationFailure as error:
raise IOError(f"failed to unlink: {error}")
except base.SMBTimeout as error:
raise TimeoutError(f"unlink: {error}")
except base.NotConnectedError as error:
raise ConnectionError(f"unlink: {error}")
smbclient.remove(self._path(fpath), **self.client_params)
except SMBException as error:
raise IOError(f"unlink: {error}")

def _read_write_fd(self, fd_from: typing.IO, fd_to: typing.IO) -> None:
while True:
data = fd_from.read(rw_chunk_size)
if not data:
break
n = 0
while n < len(data):
n += fd_to.write(data[n:])

def write(self, fpath: str, writeobj: typing.IO) -> None:
self._check_connected("write")
try:
self.ctx.storeFile(self.share, fpath, writeobj)
except smb_structs.OperationFailure as error:
raise IOError(f"failed in write_text: {error}")
except base.SMBTimeout as error:
raise TimeoutError(f"write_text: {error}")
except base.NotConnectedError as error:
raise ConnectionError(f"write: {error}")
with smbclient.open_file(
self._path(fpath), mode="wb", **self.client_params
) as fd:
self._read_write_fd(writeobj, fd)
except SMBException as error:
raise IOError(f"write: {error}")

def read(self, fpath: str, readobj: typing.IO) -> None:
self._check_connected("read")
try:
self.ctx.retrieveFile(self.share, fpath, readobj)
except smb_structs.OperationFailure as error:
raise IOError(f"failed in read_text: {error}")
except base.SMBTimeout as error:
raise TimeoutError(f"read_text: {error}")
except base.NotConnectedError as error:
raise ConnectionError(f"read: {error}")
with smbclient.open_file(
self._path(fpath), mode="rb", **self.client_params
) as fd:
self._read_write_fd(fd, readobj)
except SMBException as error:
raise IOError(f"write: {error}")

def write_text(self, fpath: str, teststr: str) -> None:
with io.BytesIO(teststr.encode()) as writeobj:
self.write(fpath, writeobj)
self._check_connected("write_text")
try:
with smbclient.open_file(
self._path(fpath), mode="w", **self.client_params
) as fd:
fd.write(teststr)
except SMBException as error:
raise IOError(f"write: {error}")

def read_text(self, fpath: str) -> str:
with io.BytesIO() as readobj:
self.read(fpath, readobj)
ret = readobj.getvalue().decode("utf8")
self._check_connected("read_text")
try:
with smbclient.open_file(
self._path(fpath), **self.client_params
) as fd:
ret = fd.read()
except SMBException as error:
raise IOError(f"write: {error}")
return ret
10 changes: 5 additions & 5 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,25 @@ deps =
pyyaml
pytest-randomly
iso8601
pysmb
commands = pytest -vrfEsxXpP testcases/
smbprotocol
commands = pytest -vrfEsxXpP testcases/ --durations=0

[testenv:pytest-unprivileged]
deps =
pytest
pyyaml
pytest-randomly
iso8601
pysmb
commands = pytest -vrfEsxXpP -k 'not privileged' testcases/
smbprotocol
commands = pytest -vrfEsxXpP -k 'not privileged' testcases/ --durations=0

[testenv:sanity]
deps =
pytest
pyyaml
pytest-randomly
iso8601
pysmb
smbprotocol
changedir = {toxinidir}
commands = pytest -vrfEsxXpP testcases/consistency

Expand Down

0 comments on commit 510167d

Please sign in to comment.