From 655bd585109cae4eab762b2ec80f0cf82c761ec6 Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Wed, 3 Jun 2026 16:11:43 -0400 Subject: [PATCH 1/9] qa/tasks: add support for embedding ssl/tls certs with templates Update template.py jinja2 templating, to add support for extracting the content of tls certs, generated by the ssl_keys task. Signed-off-by: John Mulligan --- qa/tasks/template.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/qa/tasks/template.py b/qa/tasks/template.py index 37d2b9199ff..8e57f19b887 100644 --- a/qa/tasks/template.py +++ b/qa/tasks/template.py @@ -79,6 +79,36 @@ def _role_to_remote(rctx, role): return None +@jinja2.pass_context +def _openssl_content(rctx, cert): + """Return SSL certificate contents managed by 'openssl_keys' role.""" + ctx = rctx["ctx"] + ssl_certificates = getattr(ctx, 'ssl_certificates', None) + if not ssl_certificates: + log.info("No ssl_certificates object in context") + return None + what = 'cert' + if cert.startswith('key@'): + what = 'key' + cert = cert[4:] + elif cert.startswith('cert@'): + cert = cert[5:] + + cert_obj = ssl_certificates.get(cert) + if not cert_obj: + log.info("No cert matching %r found", cert) + return None + if what == 'cert': + cfn = cert_obj.certificate + elif what == 'key': + cfn = cert_obj.key + else: + raise ValueError('invalid cert object') + content = cert_obj.remote.read_file(cfn) + log.info("Read %d from %s %s", len(content), what, cert) + return content.decode() + + def _vip_vars(rctx): """For backwards compat with the vip.subst_vip function.""" # Make it possible to replace subst_vip in vip.py. @@ -106,6 +136,7 @@ def transform(ctx, config, target, **ctx_vars): loader = jinja2.BaseLoader() jenv = jinja2.Environment(loader=loader) jenv.filters["role_to_remote"] = _role_to_remote + jenv.filters["openssl_content"] = _openssl_content setattr(ctx, "_jinja_env", jenv) rctx = dict( ctx=ctx, config=config, cluster_name=config.get("cluster", "") From bddb807fabc9d66392158eeb89461012bae61ce5 Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Mon, 29 Jun 2026 14:24:44 -0400 Subject: [PATCH 2/9] qa/tasks: pass testdir path to smb workunit Ensure that the testdir (parent dir of generated tls certs) is made available to the smb test configuration used by the workunit tests. Signed-off-by: John Mulligan --- qa/tasks/smb.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qa/tasks/smb.py b/qa/tasks/smb.py index 691b33677e1..f724a471b18 100644 --- a/qa/tasks/smb.py +++ b/qa/tasks/smb.py @@ -349,6 +349,7 @@ def _workunit_commands( def workunit(ctx, config): """Workunit wrapper with special behaviors for smb.""" from . import workunit + from teuthology import misc _config = copy.deepcopy(config) clients = _config.get('clients') or {} @@ -375,6 +376,7 @@ def workunit(ctx, config): ) _ssh_keys_config = config.get('ssh_keys', {}) _config['enable_ssh_keys'] = _ssh_keys_config not in (False, None) + _config['testdir'] = misc.get_testdir(ctx) log.info('Passing workunit config: %r', _config) with contextlib.ExitStack() as estack: if _config['enable_ssh_keys']: @@ -415,6 +417,8 @@ def write_metadata_file(ctx, config, *, roles=None): _node_info(ctx, node, client_name=node) for node in config.get('clients') ] + if config.get('testdir'): + obj['testdir'] = config['testdir'] data = json.dumps(obj) log.debug('smb metadata: %r', obj) From e77b9efd2d2aba21a1f264801806bd23bab31743 Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Mon, 29 Jun 2026 14:25:00 -0400 Subject: [PATCH 3/9] qa/workunits/smb: add an argument to set up volume mappings Add an argument to the cephadm shell wrapper function that makes it easy and obvious where to add volume mappings. This will be used in a future change to map in a directory containing TLS certificates and such. Signed-off-by: John Mulligan --- qa/workunits/smb/tests/cephutil.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qa/workunits/smb/tests/cephutil.py b/qa/workunits/smb/tests/cephutil.py index 3da118db4d5..07ddab8f870 100644 --- a/qa/workunits/smb/tests/cephutil.py +++ b/qa/workunits/smb/tests/cephutil.py @@ -56,6 +56,9 @@ def cephadm_shell_cmd( f'/home/{smb_cfg.ssh_user}/cephtest/cephadm', 'shell', ] + volumes = kwargs.pop('volumes', []) + for v in volumes: + cmd.extend(['-v', v]) cmd += list(args) proc = subprocess.run(cmd, **kwargs) if load is LoadJSON.BOTH: From b77f6fa701b5e92bb397f99761c3f6a874512568 Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Mon, 29 Jun 2026 14:25:13 -0400 Subject: [PATCH 4/9] qa/workunits/smb: add a method for getting testdir path Signed-off-by: John Mulligan --- qa/workunits/smb/tests/smbutil.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/qa/workunits/smb/tests/smbutil.py b/qa/workunits/smb/tests/smbutil.py index e345c66e464..38f7f018a57 100644 --- a/qa/workunits/smb/tests/smbutil.py +++ b/qa/workunits/smb/tests/smbutil.py @@ -1,5 +1,6 @@ import base64 import contextlib +import os import pathlib import time @@ -89,6 +90,10 @@ class SMBTestConf: # it for now until we really need to check return self.clients()[0] + @property + def testdir(self): + return self._data.get('testdir') or os.path.expanduser('~/cephtest') + @contextlib.contextmanager def connection(conf, share): From 5e4890efb7415800b426e8390c60718dda21a435 Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Mon, 29 Jun 2026 13:49:39 -0400 Subject: [PATCH 5/9] qa/workunits/smb: add test for remote control over gRPC/TCP Signed-off-by: John Mulligan --- qa/workunits/smb/tests/pytest.ini | 1 + qa/workunits/smb/tests/test_ceph_smb_ctl.py | 104 ++++++++++++-------- 2 files changed, 63 insertions(+), 42 deletions(-) diff --git a/qa/workunits/smb/tests/pytest.ini b/qa/workunits/smb/tests/pytest.ini index 982770c13c4..ae70e0874bd 100644 --- a/qa/workunits/smb/tests/pytest.ini +++ b/qa/workunits/smb/tests/pytest.ini @@ -6,4 +6,5 @@ markers = hosts_access: Host access tests rate_limiting: Rate limit tests ceph_smb_ctl_local: Local/container test of ceph-smb-ctl tool + ceph_smb_ctl_remote: Remote access test of ceph-smb-ctl tool domain: Domain integration tests diff --git a/qa/workunits/smb/tests/test_ceph_smb_ctl.py b/qa/workunits/smb/tests/test_ceph_smb_ctl.py index 9b30031c761..d8079cf49b9 100644 --- a/qa/workunits/smb/tests/test_ceph_smb_ctl.py +++ b/qa/workunits/smb/tests/test_ceph_smb_ctl.py @@ -1,3 +1,4 @@ +import pathlib import time import pytest @@ -8,14 +9,12 @@ import smbutil CEPH_SMB_CTL = 'ceph-smb-ctl' -@pytest.mark.ceph_smb_ctl_local -class TestCephSMBCtlLocal: +class RemoteCtlBase: + def _rcontrol(self, smb_cfg, args, **kwargs): + raise NotImplementedError() + def test_get_info(self, smb_cfg): - jres = cephutil.cephadm_shell_cmd( - smb_cfg, - [CEPH_SMB_CTL, 'info'], - load_json=True, - ) + jres = self._rcontrol(smb_cfg, ['info'], load_json=True) assert jres.returncode == 0 assert jres.obj assert 'samba_info' in jres.obj @@ -23,11 +22,7 @@ class TestCephSMBCtlLocal: assert 'clustered' in jres.obj['samba_info'] def test_status_empty(self, smb_cfg): - jres = cephutil.cephadm_shell_cmd( - smb_cfg, - [CEPH_SMB_CTL, 'status'], - load_json=True, - ) + jres = self._rcontrol(smb_cfg, ['status'], load_json=True) assert jres.returncode == 0 assert jres.obj assert 'server_timestamp' in jres.obj @@ -41,11 +36,7 @@ class TestCephSMBCtlLocal: with smbutil.connection(smb_cfg, share_name) as sharep: sharep.listdir() # trigger a tree connect in client lib time.sleep(0.2) - jres = cephutil.cephadm_shell_cmd( - smb_cfg, - [CEPH_SMB_CTL, 'status'], - load_json=True, - ) + jres = self._rcontrol(smb_cfg, ['status'], load_json=True) assert jres.returncode == 0 assert jres.obj assert 'server_timestamp' in jres.obj @@ -60,28 +51,23 @@ class TestCephSMBCtlLocal: def test_config_dump_samba(self, smb_cfg): share_name = smbutil.get_shares(smb_cfg)[0]['name'] - res = cephutil.cephadm_shell_cmd( - smb_cfg, - [CEPH_SMB_CTL, 'config-dump', 'samba'], - capture_output=True, - text=True, + res = self._rcontrol( + smb_cfg, ['config-dump', 'samba'], capture_output=True, text=True ) assert res.returncode == 0 assert f'[{share_name}]' in res.stdout def test_config_dump_sambacc(self, smb_cfg): - jres = cephutil.cephadm_shell_cmd( - smb_cfg, - [CEPH_SMB_CTL, 'config-dump', 'sambacc'], - load_json=True, + jres = self._rcontrol( + smb_cfg, ['config-dump', 'sambacc'], load_json=True ) assert jres.returncode == 0 assert jres.obj def test_config_dump_cmp_hash(self, smb_cfg): - res = cephutil.cephadm_shell_cmd( + res = self._rcontrol( smb_cfg, - [CEPH_SMB_CTL, 'config-dump', '--sha256', 'samba'], + ['config-dump', '--sha256', 'samba'], capture_output=True, text=True, ) @@ -93,9 +79,9 @@ class TestCephSMBCtlLocal: alg, digest = parts[-1].split(':', 1) assert alg == 'sha256' - jres = cephutil.cephadm_shell_cmd( + jres = self._rcontrol( smb_cfg, - [CEPH_SMB_CTL, 'config-summary', '--sha256', 'samba'], + ['config-summary', '--sha256', 'samba'], load_json=True, ) assert jres.returncode == 0 @@ -104,9 +90,9 @@ class TestCephSMBCtlLocal: assert jres.obj['digest']['config_digest'] == digest def test_config_shares_list(self, smb_cfg): - jres = cephutil.cephadm_shell_cmd( + jres = self._rcontrol( smb_cfg, - [CEPH_SMB_CTL, 'config-shares-list', 'samba'], + ['config-shares-list', 'samba'], load_json=True, ) assert jres.returncode == 0 @@ -118,43 +104,77 @@ class TestCephSMBCtlLocal: # the shares list in sambacc should always map exactly to the # shares list in samba itself prev_names = jres.obj - jres = cephutil.cephadm_shell_cmd( + jres = self._rcontrol( smb_cfg, - [CEPH_SMB_CTL, 'config-shares-list', 'sambacc'], + ['config-shares-list', 'sambacc'], load_json=True, ) assert jres.returncode == 0 assert sorted(jres.obj) == sorted(prev_names) def test_debug_get_set(self, smb_cfg): - jres = cephutil.cephadm_shell_cmd( + jres = self._rcontrol( smb_cfg, - [CEPH_SMB_CTL, 'get-debug-level', 'smb'], + ['get-debug-level', 'smb'], load_json=True, ) assert jres.returncode == 0, "get-debug-level smb failed" assert "debug_level" in jres.obj orig_debug_level = jres.obj["debug_level"] - jres = cephutil.cephadm_shell_cmd( + jres = self._rcontrol( smb_cfg, - [CEPH_SMB_CTL, 'set-debug-level', 'smb', "10"], + ['set-debug-level', 'smb', "10"], load_json=True, ) assert jres.returncode == 0, "set-debug-level smb 10 failed" - jres = cephutil.cephadm_shell_cmd( + jres = self._rcontrol( smb_cfg, - [CEPH_SMB_CTL, 'get-debug-level', 'smb'], + ['get-debug-level', 'smb'], load_json=True, ) assert jres.returncode == 0, "get-debug-level smb failed" assert "debug_level" in jres.obj assert jres.obj["debug_level"] == "10" - jres = cephutil.cephadm_shell_cmd( + jres = self._rcontrol( smb_cfg, - [CEPH_SMB_CTL, 'set-debug-level', 'smb', orig_debug_level], + ['set-debug-level', 'smb', orig_debug_level], load_json=True, ) assert jres.returncode == 0, "set-debug-level smb orig level failed" + + +@pytest.mark.ceph_smb_ctl_local +class TestCephSMBCtlLocal(RemoteCtlBase): + def _rcontrol(self, smb_cfg, args, **kwargs): + return cephutil.cephadm_shell_cmd( + smb_cfg, + [CEPH_SMB_CTL] + args, + **kwargs, + ) + + +@pytest.mark.ceph_smb_ctl_remote +class TestCephSMBCtlRemote(RemoteCtlBase): + def _rcontrol(self, smb_cfg, args, **kwargs): + grpc_host = f"{smb_cfg.server.ip_address}:54445" + ca_dir = pathlib.Path(smb_cfg.testdir) / 'ca' + ca_mnt = pathlib.Path('/tls') + assert (ca_dir / 'remote-control-client.crt').is_file() + assert (ca_dir / 'remote-control-client.key').is_file() + assert (ca_dir / 'rcroot.crt').is_file() + _args = [ + CEPH_SMB_CTL, + f'--address={grpc_host}', + f"--tls-cert={ca_mnt}/remote-control-client.crt", + f"--tls-key={ca_mnt}/remote-control-client.key", + f"--tls-ca-cert={ca_mnt}/rcroot.crt", + ] + return cephutil.cephadm_shell_cmd( + smb_cfg, + _args + args, + volumes=[f'{ca_dir}:{ca_mnt}:ro'], + **kwargs, + ) From 79ed468612e32862500242cc7d34d6c9158a9b75 Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Wed, 3 Jun 2026 16:11:58 -0400 Subject: [PATCH 6/9] qa/suites/cephadm/smb: add new test case for remote control grpc We recently added testing for the remote control service via the local unix socket but had no coverage for the gRPC based mode of operation. This adds a test case that configures and deploys the needed TLS certs and sets up and tests remote control with gRPC. Fixes: https://tracker.ceph.com/issues/76676 Signed-off-by: John Mulligan --- .../tasks/deploy_smb_mgr_res_remotectl.yaml | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 qa/suites/orch/cephadm/smb/tasks/deploy_smb_mgr_res_remotectl.yaml diff --git a/qa/suites/orch/cephadm/smb/tasks/deploy_smb_mgr_res_remotectl.yaml b/qa/suites/orch/cephadm/smb/tasks/deploy_smb_mgr_res_remotectl.yaml new file mode 100644 index 00000000000..38b77722168 --- /dev/null +++ b/qa/suites/orch/cephadm/smb/tasks/deploy_smb_mgr_res_remotectl.yaml @@ -0,0 +1,152 @@ +roles: +# Test is for basic smb deployment & functionality. one node cluster is OK +- - host.a + - mon.a + - mgr.x + - osd.0 + - osd.1 + - client.0 +# Reserve a host for acting as a domain controller +- - host.b + - cephadm.exclude +overrides: + ceph: + log-only-match: + - CEPHADM_ +tasks: +- openssl_keys: + rcroot: + client: client.0 + cn: rcroot + key-type: rsa:4096 + remote-control: + client: client.0 + ca: rcroot + cn: remote-control + add-san: true + remote-control-client: + client: client.0 + ca: rcroot + cn: remote-control-client + add-san: true +- smb.deploy_samba_ad_dc: + role: host.b +- cephadm: + single_host_defaults: true + +- cephadm.shell: + host.a: + - ceph fs volume create cephfs +- cephadm.wait_for_service: + service: mds.cephfs + +- cephadm.shell: + host.a: + # add subvolgroup & subvolumes for test + - cmd: ceph fs subvolumegroup create cephfs smb + - cmd: ceph fs subvolume create cephfs sv1 --group-name=smb --mode=0777 + - cmd: ceph fs subvolume create cephfs sv2 --group-name=smb --mode=0777 + # set up smb cluster and shares + - cmd: ceph mgr module enable smb + # TODO: replace sleep with poll of mgr state? + - cmd: sleep 30 + - cmd: ceph smb apply -i - + stdin: | + # --- Begin Embedded YAML + - resource_type: ceph.smb.tls.credential + tls_credential_id: rc-cert-1 + credential_type: cert + value: | + {{'cert@remote-control'|openssl_content|indent(4)}} + - resource_type: ceph.smb.tls.credential + tls_credential_id: rc-key-1 + credential_type: key + value: | + {{'key@remote-control'|openssl_content|indent(4)}} + - resource_type: ceph.smb.tls.credential + tls_credential_id: rc-ca-cert-1 + credential_type: cert + value: | + {{'cert@rcroot'|openssl_content|indent(4)}} + - resource_type: ceph.smb.cluster + cluster_id: modtest1 + auth_mode: active-directory + domain_settings: + realm: DOMAIN1.SINK.TEST + join_sources: + - source_type: resource + ref: join1-admin + custom_dns: + - "{{ctx.samba_ad_dc_ip}}" + remote_control: + cert: {ref: rc-cert-1} + key: {ref: rc-key-1} + ca_cert: {ref: rc-ca-cert-1} + placement: + count: 1 + - resource_type: ceph.smb.join.auth + auth_id: join1-admin + auth: + username: Administrator + password: Passw0rd + - resource_type: ceph.smb.share + cluster_id: modtest1 + share_id: share1 + cephfs: + volume: cephfs + subvolumegroup: smb + subvolume: sv1 + path: / + - resource_type: ceph.smb.share + cluster_id: modtest1 + share_id: share2 + cephfs: + volume: cephfs + subvolumegroup: smb + subvolume: sv2 + path: / + # --- End Embedded YAML +# Wait for the smb service to start +- cephadm.wait_for_service: + service: smb.modtest1 +# Check if shares exist +- template.exec: + host.b: + - sleep 30 + - "{{ctx.samba_client_container_cmd|join(' ')}} smbclient -U DOMAIN1\\\\ckent%1115Rose. //{{'host.a'|role_to_remote|attr('ip_address')}}/share1 -c ls" + - "{{ctx.samba_client_container_cmd|join(' ')}} smbclient -U DOMAIN1\\\\ckent%1115Rose. //{{'host.a'|role_to_remote|attr('ip_address')}}/share2 -c ls" + +- smb.workunit: + admin_node: host.a + smb_nodes: [host.a] + smb_shares: + - share1 + - share2 + timeout: 1h + clients: + client.0: + - [default, ceph_smb_ctl_remote] + +- cephadm.shell: + host.a: + - cmd: ceph smb apply -i - + stdin: | + # --- Begin Embedded YAML + - resource_type: ceph.smb.cluster + cluster_id: modtest1 + intent: removed + - resource_type: ceph.smb.join.auth + auth_id: join1-admin + intent: removed + - resource_type: ceph.smb.share + cluster_id: modtest1 + share_id: share1 + intent: removed + - resource_type: ceph.smb.share + cluster_id: modtest1 + share_id: share2 + intent: removed + # --- End Embedded YAML +# Wait for the smb service to be removed +- cephadm.wait_for_service_not_present: + service: smb.modtest1 From bb01b31129d3917a4f9c86f2f3f0162e0f402fac Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Wed, 1 Jul 2026 13:51:14 -0400 Subject: [PATCH 7/9] qa/workunits/smb: add grpc dependencies to the tests Signed-off-by: John Mulligan --- qa/workunits/smb/smb_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/workunits/smb/smb_tests.sh b/qa/workunits/smb/smb_tests.sh index 8bb33ca4b03..eda1a405649 100755 --- a/qa/workunits/smb/smb_tests.sh +++ b/qa/workunits/smb/smb_tests.sh @@ -23,5 +23,5 @@ fi trap cleanup EXIT cd "${HERE}" -"${VENV}/bin/${PY}" -m pip install pytest 'smbprotocol<1.17.0' +"${VENV}/bin/${PY}" -m pip install pytest 'smbprotocol<1.17.0' grpcio grpcio-reflection "${VENV}/bin/${PY}" -m pytest -v "$@" From f3a7fad80a88aba400d42e111e6ab0ef08dd5538 Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Wed, 1 Jul 2026 17:54:59 -0400 Subject: [PATCH 8/9] qa/workunits/smb: add option to modify share without waiting Signed-off-by: John Mulligan --- qa/workunits/smb/tests/smbutil.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qa/workunits/smb/tests/smbutil.py b/qa/workunits/smb/tests/smbutil.py index 38f7f018a57..64377f1cb3f 100644 --- a/qa/workunits/smb/tests/smbutil.py +++ b/qa/workunits/smb/tests/smbutil.py @@ -195,7 +195,7 @@ def get_share_by_id(smb_cfg, cluster_id, share_id): return None -def apply_share_config(smb_cfg, share): +def apply_share_config(smb_cfg, share, immediate=False): """Apply share configuration via the apply command.""" jres = cephutil.cephadm_shell_cmd( smb_cfg, @@ -214,5 +214,6 @@ def apply_share_config(smb_cfg, share): assert resources_ret['resource_type'] == 'ceph.smb.share' # sleep to ensure the settings got applied in smbd # TODO: make this more dynamic somehow - time.sleep(60) + if not immediate: + time.sleep(60) return resources_ret From b481de3afa0a32b3f5c409639122b14e8f49fdc0 Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Tue, 30 Jun 2026 15:58:03 -0400 Subject: [PATCH 9/9] qa/workunits/smb: add tests that use grpc api directly Add a test case that uses the API directly instead of through the ceph-smb-ctl command line. Ideally, this is validation of access tot the api and a precursor to using the grpc api as a component throughout the smb workunit tests. Signed-off-by: John Mulligan --- qa/workunits/smb/tests/test_ceph_smb_ctl.py | 105 ++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/qa/workunits/smb/tests/test_ceph_smb_ctl.py b/qa/workunits/smb/tests/test_ceph_smb_ctl.py index d8079cf49b9..025116b42d6 100644 --- a/qa/workunits/smb/tests/test_ceph_smb_ctl.py +++ b/qa/workunits/smb/tests/test_ceph_smb_ctl.py @@ -178,3 +178,108 @@ class TestCephSMBCtlRemote(RemoteCtlBase): volumes=[f'{ca_dir}:{ca_mnt}:ro'], **kwargs, ) + + +def _get_obj(value): + return value.to_simplified() + + +@pytest.mark.ceph_smb_ctl_remote +class TestCephSMBCtlAPI: + def _client(self, smb_cfg): + # alter paths to include python-common + import sys + + curr = pathlib.Path('.').absolute() + while curr.parent != pathlib.Path('/'): + pcomm = curr / 'src/python-common' + if pcomm.is_dir(): + sys.path.append(str(pcomm)) + break + curr = curr.parent + + # import the needed packges + try: + import ceph.smb.ctl.client as rclient + import ceph.smb.ctl.config as rconfig + except ImportError: + pytest.skip('failed to import ceph.smb.ctl.client OR dependency') + + # set up a grpc client w/in the test + grpc_host = f"{smb_cfg.server.ip_address}:54445" + ca_dir = pathlib.Path(smb_cfg.testdir) / 'ca' + tls_cert = ca_dir / 'remote-control-client.crt' + tls_key = ca_dir / 'remote-control-client.key' + tls_ca_cert = ca_dir / 'rcroot.crt' + assert tls_cert.is_file() + assert tls_key.is_file() + assert tls_ca_cert.is_file() + + cc = rconfig.Config( + address=grpc_host, + channel_type=rconfig.ChannelType.SECURE, + tls_cert=rconfig.TLSPath.create(tls_cert), + tls_key=rconfig.TLSPath.create(tls_key), + tls_ca_cert=rconfig.TLSPath.create(tls_ca_cert), + ) + return rclient.Client(cc) + + def test_get_info(self, smb_cfg): + obj = _get_obj(self._client(smb_cfg).info()) + assert 'samba_info' in obj + assert 'version' in obj['samba_info'] + assert 'clustered' in obj['samba_info'] + + def test_status_empty(self, smb_cfg): + obj = _get_obj(self._client(smb_cfg).status()) + assert 'server_timestamp' in obj + assert 'sessions' in obj + assert 'tree_connections' in obj + assert not obj['sessions'] + assert not obj['tree_connections'] + + def test_status_connected(self, smb_cfg): + share_name = smbutil.get_shares(smb_cfg)[0]['name'] + with smbutil.connection(smb_cfg, share_name) as sharep: + sharep.listdir() # trigger a tree connect in client lib + time.sleep(0.2) + obj = _get_obj(self._client(smb_cfg).status()) + assert obj + assert 'server_timestamp' in obj + assert 'sessions' in obj + assert 'tree_connections' in obj + assert len(obj['sessions']) == 1 + assert len(obj['tree_connections']) == 1 + assert ( + obj['sessions'][0]['session_id'] + == obj['tree_connections'][0]['session_id'] + ) + + def test_config_summary_change_poll(self, smb_cfg): + source = 'CONFIG_FOR_SAMBA' + obj = _get_obj(self._client(smb_cfg).config_summary(source)) + assert 'digest' in obj + assert 'config_digest' in obj['digest'] + prev_digest = obj['digest']['config_digest'] + time.sleep(0.2) + + # no change + obj = _get_obj(self._client(smb_cfg).config_summary(source)) + assert prev_digest == obj['digest']['config_digest'] + + # change + share_cfg = smbutil.get_shares(smb_cfg)[0] + orig_comment = share_cfg.get('comment', '') + share_cfg['comment'] = 'Foo bar baz asdf bar baz foo' + assert share_cfg['comment'] != orig_comment + smbutil.apply_share_config(smb_cfg, share_cfg, immediate=True) + + changed = False + for _ in range(0, 150): + time.sleep(0.5) + obj = _get_obj(self._client(smb_cfg).config_summary(source)) + changed = prev_digest != obj['digest']['config_digest'] + if changed: + break + + assert changed, "config digest never changed"