|
7 | 7 | # SPDX-License-Identifier: GPL-3.0-or-later |
8 | 8 |
|
9 | 9 | from __future__ import absolute_import, division, print_function |
| 10 | + |
10 | 11 | __metaclass__ = type |
11 | 12 |
|
12 | 13 | DOCUMENTATION = r""" |
|
95 | 96 | """ |
96 | 97 |
|
97 | 98 |
|
98 | | -from ansible.module_utils.basic import AnsibleModule |
| 99 | +from ansible_collections.community.general.plugins.module_utils.module_helper import StateModuleHelper |
| 100 | + |
| 101 | +import os |
99 | 102 | import re |
100 | 103 |
|
101 | 104 |
|
102 | | -class Sysrc(object): |
103 | | - def __init__(self, module, name, value, path, delim, jail): |
104 | | - self.module = module |
105 | | - self.name = name |
106 | | - self.changed = False |
107 | | - self.value = value |
108 | | - self.path = path |
109 | | - self.delim = delim |
110 | | - self.jail = jail |
111 | | - self.sysrc = module.get_bin_path('sysrc', True) |
112 | | - |
113 | | - def has_unknown_variable(self, out, err): |
114 | | - # newer versions of sysrc use stderr instead of stdout |
115 | | - return err.find("unknown variable") > 0 or out.find("unknown variable") > 0 |
116 | | - |
117 | | - def exists(self): |
118 | | - """ |
119 | | - Tests whether the name is in the file. If parameter value is defined, |
120 | | - then tests whether name=value is in the file. These tests are necessary |
121 | | - because sysrc doesn't use exit codes. Instead, let sysrc read the |
122 | | - file's content and create a dictionary comprising the configuration. |
123 | | - Use this dictionary to preform the tests. |
124 | | - """ |
125 | | - (rc, out, err) = self.run_sysrc('-e', '-a') |
126 | | - conf = dict([i.split('=', 1) for i in out.splitlines()]) |
127 | | - if self.value is None: |
128 | | - return self.name in conf |
129 | | - else: |
130 | | - return self.name in conf and conf[self.name] == '"%s"' % self.value |
131 | | - |
132 | | - def contains(self): |
133 | | - (rc, out, err) = self.run_sysrc('-n', self.name) |
134 | | - if self.has_unknown_variable(out, err): |
135 | | - return False |
136 | | - |
137 | | - return self.value in out.strip().split(self.delim) |
138 | | - |
139 | | - def present(self): |
140 | | - if self.exists(): |
141 | | - return |
| 105 | +class Sysrc(StateModuleHelper): |
| 106 | + module = dict( |
| 107 | + argument_spec=dict( |
| 108 | + name=dict(type='str', required=True), |
| 109 | + value=dict(type='str', default=None), |
| 110 | + state=dict(type='str', default='present', choices=['absent', 'present', 'value_present', 'value_absent']), |
| 111 | + path=dict(type='str', default='/etc/rc.conf'), |
| 112 | + delim=dict(type='str', default=' '), |
| 113 | + jail=dict(type='str', default=None) |
| 114 | + ), |
| 115 | + supports_check_mode=True |
| 116 | + ) |
| 117 | + output_params = ('value',) |
| 118 | + use_old_vardict = False |
142 | 119 |
|
143 | | - if not self.module.check_mode: |
144 | | - (rc, out, err) = self.run_sysrc("%s=%s" % (self.name, self.value)) |
| 120 | + def __init_module__(self): |
| 121 | + # OID style names are not supported |
| 122 | + if not re.match(r'^\w+$', self.vars.name, re.ASCII): |
| 123 | + self.module.fail_json(msg="Name may only contain alpha-numeric and underscore characters") |
145 | 124 |
|
146 | | - self.changed = True |
| 125 | + self.sysrc = self.module.get_bin_path('sysrc', True) |
147 | 126 |
|
148 | | - def absent(self): |
149 | | - if not self.exists(): |
150 | | - return |
| 127 | + def _contains(self): |
| 128 | + value = self._get() |
| 129 | + if value is None: |
| 130 | + return False, None |
151 | 131 |
|
152 | | - # inversed since we still need to mark as changed |
153 | | - if not self.module.check_mode: |
154 | | - (rc, out, err) = self.run_sysrc('-x', self.name) |
155 | | - if self.has_unknown_variable(out, err): |
156 | | - return |
| 132 | + value = value.split(self.vars.delim) |
157 | 133 |
|
158 | | - self.changed = True |
| 134 | + return self.vars.value in value, value |
159 | 135 |
|
160 | | - def value_present(self): |
161 | | - if self.contains(): |
162 | | - return |
| 136 | + def _get(self): |
| 137 | + if not os.path.exists(self.vars.path): |
| 138 | + return None |
163 | 139 |
|
164 | | - if self.module.check_mode: |
165 | | - self.changed = True |
166 | | - return |
| 140 | + (rc, out, err) = self._sysrc('-v', '-n', self.vars.name) |
| 141 | + if "unknown variable" in err or "unknown variable" in out: |
| 142 | + # Prior to FreeBSD 11.1 sysrc would write "unknown variable" to stdout and not stderr |
| 143 | + # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=229806 |
| 144 | + return None |
167 | 145 |
|
168 | | - setstring = '%s+=%s%s' % (self.name, self.delim, self.value) |
169 | | - (rc, out, err) = self.run_sysrc(setstring) |
170 | | - if out.find("%s:" % self.name) == 0: |
171 | | - values = out.split(' -> ')[1].strip().split(self.delim) |
172 | | - if self.value in values: |
173 | | - self.changed = True |
| 146 | + if out.startswith(self.vars.path): |
| 147 | + return out.split(':', 1)[1].strip() |
174 | 148 |
|
175 | | - def value_absent(self): |
176 | | - if not self.contains(): |
177 | | - return |
| 149 | + return None |
178 | 150 |
|
179 | | - if self.module.check_mode: |
180 | | - self.changed = True |
181 | | - return |
| 151 | + def _modify(self, op, changed): |
| 152 | + (rc, out, err) = self._sysrc("%s%s=%s%s" % (self.vars.name, op, self.vars.delim, self.vars.value)) |
| 153 | + if out.startswith("%s:" % self.vars.name): |
| 154 | + return changed(out.split(' -> ')[1].strip().split(self.vars.delim)) |
182 | 155 |
|
183 | | - setstring = '%s-=%s%s' % (self.name, self.delim, self.value) |
184 | | - (rc, out, err) = self.run_sysrc(setstring) |
185 | | - if out.find("%s:" % self.name) == 0: |
186 | | - values = out.split(' -> ')[1].strip().split(self.delim) |
187 | | - if self.value not in values: |
188 | | - self.changed = True |
189 | | - |
190 | | - def run_sysrc(self, *args): |
191 | | - cmd = [self.sysrc, '-f', self.path] |
192 | | - if self.jail: |
193 | | - cmd += ['-j', self.jail] |
| 156 | + return False |
| 157 | + |
| 158 | + def _sysrc(self, *args): |
| 159 | + cmd = [self.sysrc, '-f', self.vars.path] |
| 160 | + if self.vars.jail: |
| 161 | + cmd += ['-j', self.vars.jail] |
194 | 162 | cmd.extend(args) |
195 | 163 |
|
196 | 164 | (rc, out, err) = self.module.run_command(cmd) |
| 165 | + if "Permission denied" in err: |
| 166 | + self.module.fail_json(msg="Permission denied for %s" % self.vars.path) |
197 | 167 |
|
198 | | - return (rc, out, err) |
| 168 | + return rc, out, err |
199 | 169 |
|
| 170 | + def state_absent(self): |
| 171 | + if self._get() is None: |
| 172 | + return |
200 | 173 |
|
201 | | -def main(): |
202 | | - module = AnsibleModule( |
203 | | - argument_spec=dict( |
204 | | - name=dict(type='str', required=True), |
205 | | - value=dict(type='str', default=None), |
206 | | - state=dict(type='str', default='present', choices=['absent', 'present', 'value_present', 'value_absent']), |
207 | | - path=dict(type='str', default='/etc/rc.conf'), |
208 | | - delim=dict(type='str', default=' '), |
209 | | - jail=dict(type='str', default=None), |
210 | | - ), |
211 | | - supports_check_mode=True, |
212 | | - ) |
| 174 | + if not self.check_mode: |
| 175 | + self._sysrc('-x', self.vars.name) |
213 | 176 |
|
214 | | - name = module.params.pop('name') |
215 | | - # OID style names are not supported |
216 | | - if not re.match('^[a-zA-Z0-9_]+$', name): |
217 | | - module.fail_json( |
218 | | - msg="Name may only contain alphanumeric and underscore characters" |
219 | | - ) |
220 | | - |
221 | | - value = module.params.pop('value') |
222 | | - state = module.params.pop('state') |
223 | | - path = module.params.pop('path') |
224 | | - delim = module.params.pop('delim') |
225 | | - jail = module.params.pop('jail') |
226 | | - result = dict( |
227 | | - name=name, |
228 | | - state=state, |
229 | | - value=value, |
230 | | - path=path, |
231 | | - delim=delim, |
232 | | - jail=jail |
233 | | - ) |
| 177 | + self.changed = True |
| 178 | + |
| 179 | + def state_present(self): |
| 180 | + value = self._get() |
| 181 | + if value == self.vars.value: |
| 182 | + return |
| 183 | + |
| 184 | + if self.vars.value is None: |
| 185 | + self.vars.set('value', value) |
| 186 | + return |
| 187 | + |
| 188 | + if not self.check_mode: |
| 189 | + self._sysrc("%s=%s" % (self.vars.name, self.vars.value)) |
| 190 | + |
| 191 | + self.changed = True |
234 | 192 |
|
235 | | - rc_value = Sysrc(module, name, value, path, delim, jail) |
| 193 | + def state_value_absent(self): |
| 194 | + (contains, _unused) = self._contains() |
| 195 | + if not contains: |
| 196 | + return |
236 | 197 |
|
237 | | - if state == 'present': |
238 | | - rc_value.present() |
239 | | - elif state == 'absent': |
240 | | - rc_value.absent() |
241 | | - elif state == 'value_present': |
242 | | - rc_value.value_present() |
243 | | - elif state == 'value_absent': |
244 | | - rc_value.value_absent() |
| 198 | + self.changed = self.check_mode or self._modify('-', lambda values: self.vars.value not in values) |
245 | 199 |
|
246 | | - result['changed'] = rc_value.changed |
| 200 | + def state_value_present(self): |
| 201 | + (contains, value) = self._contains() |
| 202 | + if contains: |
| 203 | + return |
247 | 204 |
|
248 | | - module.exit_json(**result) |
| 205 | + if self.vars.value is None: |
| 206 | + self.vars.set('value', value) |
| 207 | + return |
| 208 | + |
| 209 | + self.changed = self.check_mode or self._modify('+', lambda values: self.vars.value in values) |
| 210 | + |
| 211 | + |
| 212 | +def main(): |
| 213 | + Sysrc.execute() |
249 | 214 |
|
250 | 215 |
|
251 | 216 | if __name__ == '__main__': |
|
0 commit comments