sysrc: refactor

This commit is contained in:
David Lundgren 2025-07-14 16:32:50 -05:00
commit ad88e203d7
No known key found for this signature in database
GPG key ID: 2B78584C498D2A5E
3 changed files with 106 additions and 75 deletions

View file

@ -117,82 +117,73 @@ class Sysrc(object):
self.jail = jail self.jail = jail
self.sysrc = module.get_bin_path('sysrc', True) self.sysrc = module.get_bin_path('sysrc', True)
def has_unknown_variable(self, out, err): def load_sysrc_value(self):
# newer versions of sysrc use stderr instead of stdout """
return err.find("unknown variable") > 0 or out.find("unknown variable") > 0 Loads the value that sysrc has for the given variable. This checks that the variable exists in the file first
so that a default value is not loaded and then counted as changed.
"""
# if the file doesn't exist, then there is no need to load it
if not os.path.exists(self.path):
return None
def exists(self): # Check if the name exists in the file 0 = true, 1 = false
""" (rc, out, err) = self.run_sysrc('-c', self.name)
Tests whether the name is in the file. If parameter value is defined, if rc == 1:
then tests whether name=value is in the file. These tests are necessary return None
because sysrc doesn't use exit codes. Instead, let sysrc read the
file's content and create a dictionary comprising the configuration. (rc, out, err) = self.run_sysrc('-n', self.name)
Use this dictionary to preform the tests. if err.find("unknown variable") > 0 or out.find("unknown variable") > 0:
""" return None
(rc, out, err) = self.run_sysrc('-e', '-a')
conf = dict([i.split('=', 1) for i in out.splitlines()]) return out.strip()
if self.value is None:
return self.name in conf
else:
return self.name in conf and conf[self.name] == '"%s"' % self.value
def contains(self): def contains(self):
(rc, out, err) = self.run_sysrc('-n', self.name) value = self.load_sysrc_value()
if self.has_unknown_variable(out, err): if value is None:
return False return False
return self.value in out.strip().split(self.delim) return self.value in value.split(self.delim)
def modify(self, op, changed):
(rc, out, err) = self.run_sysrc('%s%s=%s%s' % (self.name, op, self.delim, self.value))
if out.find("%s:" % self.name) == 0:
return changed(out.split(' -> ')[1].strip().split(self.delim))
def present(self): def present(self):
if self.exists(): if self.load_sysrc_value() == self.value:
return return False
if not self.module.check_mode: if not self.module.check_mode:
(rc, out, err) = self.run_sysrc("%s=%s" % (self.name, self.value)) self.run_sysrc("%s=%s" % (self.name, self.value))
self.changed = True return True
def absent(self): def absent(self):
if not self.exists(): if self.load_sysrc_value() is None:
return return False
# inversed since we still need to mark as changed
if not self.module.check_mode: if not self.module.check_mode:
(rc, out, err) = self.run_sysrc('-x', self.name) self.run_sysrc('-x', self.name)
if self.has_unknown_variable(out, err):
return
self.changed = True return True
def value_present(self): def value_present(self):
if self.contains(): if self.contains():
return return False
if self.module.check_mode: if self.module.check_mode or self.modify('+', lambda values: self.value in values):
self.changed = True return True
return
setstring = '%s+=%s%s' % (self.name, self.delim, self.value) return False
(rc, out, err) = self.run_sysrc(setstring)
if out.find("%s:" % self.name) == 0:
values = out.split(' -> ')[1].strip().split(self.delim)
if self.value in values:
self.changed = True
def value_absent(self): def value_absent(self):
if not self.contains(): if not self.contains():
return return False
if self.module.check_mode: if self.module.check_mode or self.modify('-', lambda values: self.value not in values):
self.changed = True return True
return
setstring = '%s-=%s%s' % (self.name, self.delim, self.value) return False
(rc, out, err) = self.run_sysrc(setstring)
if out.find("%s:" % self.name) == 0:
values = out.split(' -> ')[1].strip().split(self.delim)
if self.value not in values:
self.changed = True
def run_sysrc(self, *args): def run_sysrc(self, *args):
cmd = [self.sysrc, '-f', self.path] cmd = [self.sysrc, '-f', self.path]
@ -225,32 +216,17 @@ def main():
msg="Name may only contain alphanumeric and underscore characters" msg="Name may only contain alphanumeric and underscore characters"
) )
value = module.params.pop('value')
state = module.params.pop('state')
path = module.params.pop('path')
delim = module.params.pop('delim')
jail = module.params.pop('jail')
result = dict( result = dict(
name=name, name=name,
state=state, state=module.params.pop('state'),
value=value, value=module.params.pop('value'),
path=path, path=module.params.pop('path'),
delim=delim, delim=module.params.pop('delim'),
jail=jail jail=module.params.pop('jail')
) )
rc_value = Sysrc(module, name, value, path, delim, jail) sysrc = Sysrc(module, name, result['value'], result['path'], result['delim'], result['jail'])
result['changed'] = getattr(sysrc, result['state'])()
if state == 'present':
rc_value.present()
elif state == 'absent':
rc_value.absent()
elif state == 'value_present':
rc_value.value_present()
elif state == 'value_absent':
rc_value.value_absent()
result['changed'] = rc_value.changed
module.exit_json(**result) module.exit_json(**result)

View file

@ -0,0 +1,4 @@
k1="v1"
jail_list="
foo
bar"

View file

@ -369,14 +369,65 @@
- "value_2 == sysrc_equals_sign_2.value" - "value_2 == sysrc_equals_sign_2.value"
- "value_2 == conf.spamd_flags" - "value_2 == conf.spamd_flags"
##
## sysrc - #10004 state=absent when using default settings will report `changed=true`
##
- name: Test that a key from /etc/defaults/rc.conf is not used to mark changed
sysrc:
name: dumpdev
state: absent
path: /tmp/10004.conf
register: sysrc_10004_absent
- name: Ensure that the defaults are not consulted
assert:
that:
- not sysrc_10004_absent.changed
- name: Test that a delimited key from /etc/defaults/rc.conf is not used to mark changed
sysrc:
name: rc_conf_files
state: value_absent
path: /tmp/10004.conf
register: sysrc_10004_value_absent
- name: Ensure that the default is not consulted
assert:
that:
- not sysrc_10004_value_absent.changed
##
## sysrc - #10394 Ensure that files with multi-line values work
##
- name: Copy 10394.conf
copy:
src: 10394.conf
dest: /tmp/10394.conf
- name: Change value for k1
sysrc:
name: k1
value: v2
register: sysrc_10394_changed
- name: Get file content
shell: "cat /tmp/10394.conf"
register: sysrc_10394_content
- name: Ensure sysrc changed k1 from v1 to v2
assert:
that:
- sysrc_10394_changed.changed
- "'k1=\"v2\"' in sysrc_10394_content.stdout_lines"
always: always:
- name: Restore /etc/rc.conf - name: Restore /etc/rc.conf
copy: copy:
content: "{{ cached_etc_rcconf_content }}" content: "{{ cached_etc_rcconf_content.stdout }}"
dest: /etc/rc.conf dest: /etc/rc.conf
- name: Restore /boot/loader.conf - name: Restore /boot/loader.conf
copy: copy:
content: "{{ cached_boot_loaderconf_content }}" content: "{{ cached_boot_loaderconf_content.stdout }}"
dest: /boot/loader.conf dest: /boot/loader.conf