Skip to content

Commit

Permalink
qa/tasks: add a new cephadm task for setting up samba ad dc
Browse files Browse the repository at this point in the history
Add a new task function to cephadm.py that sets up a container running
the Samba based domain controller on a node using podman or docker.
Much of the function actually deals with disabling systemd-resolved
because that service conflicts with the DNS server component of the DC.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
  • Loading branch information
phlogistonjohn committed Mar 21, 2024
1 parent 4e897de commit a99dc99
Showing 1 changed file with 250 additions and 0 deletions.
250 changes: 250 additions & 0 deletions qa/tasks/cephadm.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import logging
import os
import re
import time
import uuid
import yaml

Expand All @@ -24,6 +25,7 @@
from teuthology.orchestra import run
from teuthology.orchestra.daemon import DaemonGroup
from teuthology.config import config as teuth_config
from teuthology.exceptions import ConfigError, CommandFailedError
from textwrap import dedent
from tasks.cephfs.filesystem import MDSCluster, Filesystem
from tasks.util import chacra
Expand Down Expand Up @@ -1746,6 +1748,254 @@ def initialize_config(ctx, config):
yield


def _disable_systemd_resolved(ctx, remote):
r = remote.run(args=['ss', '-lunH'], stdout=StringIO())
# this heuristic tries to detect if systemd-resolved is running
if '%lo:53' not in r.stdout.getvalue():
return
log.info('Disabling systemd-resolved on %s', remote.shortname)
# Samba AD DC container DNS support conflicts with resolved stub
# resolver when using host networking. And we want host networking
# because it is the simplest thing to set up. We therefore will turn
# off the stub resolver.
r = remote.run(
args=['sudo', 'cat', '/etc/systemd/resolved.conf'],
stdout=StringIO(),
)
resolved_conf = r.stdout.getvalue()
setattr(ctx, 'orig_resolved_conf', resolved_conf)
new_resolved_conf = (
resolved_conf + '\n# EDITED BY TEUTHOLOGY: deploy_samba_ad_dc\n'
)
if '[Resolve]' not in new_resolved_conf.splitlines():
new_resolved_conf += '[Resolve]\n'
new_resolved_conf += 'DNSStubListener=no\n'
remote.write_file(
path='/etc/systemd/resolved.conf',
data=new_resolved_conf,
sudo=True,
)
remote.run(args=['sudo', 'systemctl', 'restart', 'systemd-resolved'])
r = remote.run(args=['ss', '-lunH'], stdout=StringIO())
assert '%lo:53' not in r.stdout.getvalue()
# because docker is a big fat persistent deamon, we need to bounce it
# after resolved is restarted
remote.run(args=['sudo', 'systemctl', 'restart', 'docker'])


def _reset_systemd_resolved(ctx, remote):
orig_resolved_conf = getattr(ctx, 'orig_resolved_conf', None)
if not orig_resolved_conf:
return # no orig_resolved_conf means nothing to reset
log.info('Resetting systemd-resolved state on %s', remote.shortname)
remote.write_file(
path='/etc/systemd/resolved.conf',
data=orig_resolved_conf,
sudo=True,
)
remote.run(args=['sudo', 'systemctl', 'restart', 'systemd-resolved'])
setattr(ctx, 'orig_resolved_conf', None)


def _samba_ad_dc_conf(ctx, remote, cengine):
# this config has not been tested outside of smithi nodes. it's possible
# that this will break when used elsewhere because we have to list
# interfaces explicitly. Later I may add a feature to sambacc to exclude
# known-unwanted interfaces that having to specify known good interfaces.
cf = {
"samba-container-config": "v0",
"configs": {
"demo": {
"instance_features": ["addc"],
"domain_settings": "sink",
"instance_name": "dc1",
}
},
"domain_settings": {
"sink": {
"realm": "DOMAIN1.SINK.TEST",
"short_domain": "DOMAIN1",
"admin_password": "Passw0rd",
"interfaces": {
"exclude_pattern": "^docker[0-9]+$",
},
}
},
"domain_groups": {
"sink": [
{"name": "supervisors"},
{"name": "employees"},
{"name": "characters"},
{"name": "bulk"},
]
},
"domain_users": {
"sink": [
{
"name": "bwayne",
"password": "1115Rose.",
"given_name": "Bruce",
"surname": "Wayne",
"member_of": ["supervisors", "characters", "employees"],
},
{
"name": "ckent",
"password": "1115Rose.",
"given_name": "Clark",
"surname": "Kent",
"member_of": ["characters", "employees"],
},
{
"name": "user0",
"password": "1115Rose.",
"given_name": "George0",
"surname": "Hue-Sir",
"member_of": ["bulk"],
},
{
"name": "user1",
"password": "1115Rose.",
"given_name": "George1",
"surname": "Hue-Sir",
"member_of": ["bulk"],
},
{
"name": "user2",
"password": "1115Rose.",
"given_name": "George2",
"surname": "Hue-Sir",
"member_of": ["bulk"],
},
{
"name": "user3",
"password": "1115Rose.",
"given_name": "George3",
"surname": "Hue-Sir",
"member_of": ["bulk"],
},
]
},
}
cf_json = json.dumps(cf)
remote.run(args=['sudo', 'mkdir', '-p', '/var/tmp/samba'])
remote.write_file(
path='/var/tmp/samba/container.json', data=cf_json, sudo=True
)
return [
'--volume=/var/tmp/samba:/etc/samba-container:ro',
'-eSAMBACC_CONFIG=/etc/samba-container/container.json',
]


@contextlib.contextmanager
def deploy_samba_ad_dc(ctx, config):
role = config.get('role')
ad_dc_image = config.get(
'ad_dc_image', 'quay.io/samba.org/samba-ad-server:latest'
)
samba_client_image = config.get(
'samba_client_image', 'quay.io/samba.org/samba-client:latest'
)
test_user_pass = config.get('test_user_pass', 'DOMAIN1\\ckent%1115Rose.')
if not role:
raise ConfigError(
"you must specify a role to allocate a host for the AD DC"
)
(remote,) = ctx.cluster.only(role).remotes.keys()
ip = remote.ssh.get_transport().getpeername()[0]
cengine = 'podman'
try:
log.info("Testing if podman is available")
remote.run(args=['sudo', cengine, '--help'])
except CommandFailedError:
log.info("Failed to find podman. Using docker")
cengine = 'docker'
remote.run(args=['sudo', cengine, 'pull', ad_dc_image])
remote.run(args=['sudo', cengine, 'pull', samba_client_image])
_disable_systemd_resolved(ctx, remote)
remote.run(
args=[
'sudo',
'mkdir',
'-p',
'/var/lib/samba/container/logs',
'/var/lib/samba/container/data',
]
)
remote.run(
args=[
'sudo',
cengine,
'run',
'-d',
'--name=samba-ad',
'--network=host',
'--privileged',
]
+ _samba_ad_dc_conf(ctx, remote, cengine)
+ [ad_dc_image]
)

# test that the ad dc is running and basically works
connected = False
samba_client_container_cmd = [
'sudo',
cengine,
'run',
'--rm',
'--net=host',
f'--dns={ip}',
'-eKRB5_CONFIG=/dev/null',
samba_client_image,
]
for idx in range(10):
time.sleep((2 ** (1 + idx)) / 8)
log.info("Probing SMB status of DC %s, idx=%s", ip, idx)
cmd = samba_client_container_cmd + [
'smbclient',
'-U',
test_user_pass,
'//domain1.sink.test/sysvol',
'-c',
'ls',
]
try:
remote.run(args=cmd)
connected = True
log.info("SMB status probe succeeded")
break
except CommandFailedError:
pass
if not connected:
raise RuntimeError('failed to connect to AD DC SMB share')

setattr(ctx, 'samba_ad_dc_ip', ip)
setattr(ctx, 'samba_client_container_cmd', samba_client_container_cmd)
try:
yield
finally:
try:
remote.run(args=['sudo', cengine, 'stop', 'samba-ad'])
except CommandFailedError:
log.error("Failed to stop samba-ad container")
try:
remote.run(args=['sudo', cengine, 'rm', 'samba-ad'])
except CommandFailedError:
log.error("Failed to remove samba-ad container")
remote.run(
args=[
'sudo',
'rm',
'-rf',
'/var/lib/samba/container/logs',
'/var/lib/samba/container/data',
]
)
_reset_systemd_resolved(ctx, remote)
setattr(ctx, 'samba_ad_dc_ip', None)
setattr(ctx, 'samba_client_container_cmd', None)


@contextlib.contextmanager
def task(ctx, config):
"""
Expand Down

0 comments on commit a99dc99

Please sign in to comment.