16
16
# along with this program. If not, see <http://www.gnu.org/licenses/>
17
17
#
18
18
19
+ import argparse
20
+ import functools
21
+ import logging
22
+ import subprocess
19
23
import sys
24
+ import typing
20
25
26
+ from sambacc import config
27
+ from sambacc import samba_cmds
28
+ from sambacc .simple_waiter import watch
21
29
import sambacc .netcmd_loader as nc
22
30
import sambacc .paths as paths
23
31
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__ )
25
41
26
42
27
43
@commands .command (name = "print-config" )
@@ -43,3 +59,96 @@ def import_config(ctx: Context) -> None:
43
59
44
60
loader = nc .NetCmdLoader ()
45
61
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