Skip to content

Commit b755c5b

Browse files
commands: add an update-config command
The update-config command is used to update the smb configuration for an existing instance. The --watch option causes the command to loop: monitoring the configuration JSON for change and reacting to the change if it occurs. Signed-off-by: John Mulligan <[email protected]>
1 parent 3258cd4 commit b755c5b

File tree

1 file changed

+110
-1
lines changed

1 file changed

+110
-1
lines changed

sambacc/commands/config.py

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,28 @@
1616
# along with this program. If not, see <http://www.gnu.org/licenses/>
1717
#
1818

19+
import argparse
20+
import functools
21+
import logging
22+
import subprocess
1923
import sys
24+
import typing
2025

26+
from sambacc import config
27+
from sambacc import samba_cmds
28+
from sambacc.simple_waiter import watch
2129
import sambacc.netcmd_loader as nc
2230
import sambacc.paths as paths
2331

24-
from .cli import commands, setup_steps, Context
32+
from .cli import (
33+
Context,
34+
best_leader_locator,
35+
best_waiter,
36+
commands,
37+
setup_steps,
38+
)
39+
40+
_logger = logging.getLogger(__name__)
2541

2642

2743
@commands.command(name="print-config")
@@ -43,3 +59,96 @@ def import_config(ctx: Context) -> None:
4359

4460
loader = nc.NetCmdLoader()
4561
loader.import_config(ctx.instance_config)
62+
63+
64+
def _update_config_args(parser: argparse.ArgumentParser) -> None:
65+
parser.add_argument(
66+
"--watch",
67+
action="store_true",
68+
help=("If set, watch the source for changes and update config."),
69+
)
70+
71+
72+
def _read_config(ctx: Context) -> config.InstanceConfig:
73+
cfgs = ctx.cli.config or []
74+
return config.read_config_files(cfgs).get(ctx.cli.identity)
75+
76+
77+
def _update_config(
78+
current: config.InstanceConfig,
79+
previous: typing.Optional[config.InstanceConfig],
80+
ensure_paths: bool = True,
81+
notify_server: bool = True,
82+
) -> typing.Tuple[config.InstanceConfig, bool]:
83+
"""Compare the current and previous instance configurations. If they
84+
differ, ensure any new paths, update the samba config, and inform any
85+
running smbds of the new configuration. Return the current config and a
86+
boolean indicating if the instance configs differed.
87+
"""
88+
# has the config changed?
89+
changed = current != previous
90+
# ensure share paths exist
91+
if changed and ensure_paths:
92+
for share in current.shares():
93+
path = share.path()
94+
if not path:
95+
continue
96+
_logger.info(f"Ensuring share path: {path}")
97+
paths.ensure_share_dirs(path)
98+
# update smb config
99+
if changed:
100+
_logger.info("Updating samba configuration")
101+
loader = nc.NetCmdLoader()
102+
loader.import_config(current)
103+
# notify smbd of changes
104+
if changed and notify_server:
105+
subprocess.check_call(
106+
list(samba_cmds.smbcontrol["smbd", "reload-config"])
107+
)
108+
return current, changed
109+
110+
111+
def _exec_if_leader(
112+
ctx: Context,
113+
cond_func: typing.Callable[..., typing.Tuple[config.InstanceConfig, bool]],
114+
) -> typing.Callable[..., typing.Tuple[config.InstanceConfig, bool]]:
115+
"""Run the cond func only on "nodes" that are the cluster leader."""
116+
# CTDB status and leader detection is not changeable at runtime.
117+
# we do not need to account for it changing in the updated config file(s)
118+
@functools.wraps(cond_func)
119+
def _call_if_leader(
120+
current: config.InstanceConfig, previous: config.InstanceConfig
121+
) -> typing.Tuple[config.InstanceConfig, bool]:
122+
with best_leader_locator(ctx.instance_config) as ll:
123+
if not ll.is_leader():
124+
_logger.info("skipping config update. node not leader")
125+
return previous, False
126+
_logger.info("checking for update. node is leader")
127+
return cond_func(current, previous)
128+
129+
return _call_if_leader
130+
131+
132+
@commands.command(name="update-config", arg_func=_update_config_args)
133+
def update_config(ctx: Context) -> None:
134+
_get_config = functools.partial(_read_config, ctx)
135+
_cmp_func = _update_config
136+
137+
if ctx.instance_config.with_ctdb:
138+
_logger.info("enabling ctdb support: will check for leadership")
139+
_cmp_func = _exec_if_leader(ctx, _cmp_func)
140+
141+
if ctx.cli.watch:
142+
_logger.info("will watch configuration source")
143+
waiter = best_waiter(ctx.cli.config)
144+
watch(
145+
waiter,
146+
ctx.instance_config,
147+
_get_config,
148+
_cmp_func,
149+
)
150+
else:
151+
# we pass None as the previous config so that the command is
152+
# not nearly always a no-op when run from the command line.
153+
_cmp_func(_get_config(), None)
154+
return

0 commit comments

Comments
 (0)