Skip to content

Instantly share code, notes, and snippets.

@jctanner
Created January 13, 2014 23:03
Show Gist options
  • Save jctanner/8409824 to your computer and use it in GitHub Desktop.
Save jctanner/8409824 to your computer and use it in GitHub Desktop.
sysctl rewrite
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2012, David "DaviXX" CHANIAL <[email protected]>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
DOCUMENTATION = '''
---
module: sysctl
short_description: Manage entries in sysctl.conf.
description:
- This module manipulates sysctl entries and optionally performs a C(/sbin/sysctl -p) after changing them.
version_added: "1.0"
options:
name:
description:
- The dot-separated path (aka I(key)) specifying the sysctl variable.
required: true
default: null
aliases: [ 'key' ]
value:
description:
- Desired value of the sysctl key.
required: false
default: null
aliases: [ 'val' ]
state:
description:
- Whether the entry should be present or absent.
choices: [ "present", "absent" ]
default: present
checks:
description:
- If C(none), no smart/facultative checks will be made. If
C(before), some checks are performed before any update (i.e. is
the sysctl key writable?). If C(after), some checks are performed
after an update (i.e. does kernel return the set value?). If
C(both), all of the smart checks (C(before) and C(after)) are
performed.
choices: [ "none", "before", "after", "both" ]
default: both
reload:
description:
- If C(yes), performs a I(/sbin/sysctl -p) if the C(sysctl_file) is
updated. If C(no), does not reload I(sysctl) even if the
C(sysctl_file) is updated.
choices: [ "yes", "no" ]
default: "yes"
sysctl_file:
description:
- Specifies the absolute path to C(sysctl.conf), if not C(/etc/sysctl.conf).
required: false
default: /etc/sysctl.conf
notes: []
requirements: []
author: David "DaviXX" CHANIAL <[email protected]>
'''
EXAMPLES = '''
# Set vm.swappiness to 5 in /etc/sysctl.conf
- sysctl: name=vm.swappiness value=5 state=present
# Remove kernel.panic entry from /etc/sysctl.conf
- sysctl: name=kernel.panic state=absent sysctl_file=/etc/sysctl.conf
# Set kernel.panic to 3 in /tmp/test_sysctl.conf, check if the sysctl key
# seems writable, but do not reload sysctl, and do not check kernel value
# after (not needed, because the real /etc/sysctl.conf was not updated)
- sysctl: name=kernel.panic value=3 sysctl_file=/tmp/test_sysctl.conf checks=before reload=no
'''
# ==============================================================
import os
import tempfile
import re
class SysctlModule(object):
def __init__(self, module):
self.module = module
self.args = self.module.params
self.sysctl_cmd = self.module.get_bin_path('sysctl', required=True)
self.sysctl_file = self.args['sysctl_file']
self.proc_value = None # current token value in proc fs
self.file_value = None # current token value in file
self.file_lines = [] # all lines in the file
self.file_values = {} # dict of token values
self.changed = False # will change occur
self.set_proc = False # does sysctl need to set value
self.write_file = False # does the sysctl file need to be reloaded
self.process()
# ==============================================================
# LOGIC
# ==============================================================
def process(self):
self.args['name'] = self.args['name'].strip()
self.args['value'] = self.args['value'].strip()
thisname = self.args['name']
# get the current proc fs value
self.proc_value = self.get_token_curr_value(thisname)
# get the currect sysctl file value
self.read_sysctl_file()
if thisname not in self.file_values:
self.file_values[thisname] = None
else:
#import epdb; epdb.serve()
pass
# update file contents with desired token/value
self.fix_lines()
#open("/tmp/awx.log", "a").write("proc_value: %s\n" % self.proc_value)
#open("/tmp/awx.log", "a").write("file_value: %s\n" % self.file_value)
# what do we need to do now?
if self.file_values[thisname] is None and self.args['state'] == "present":
open("/tmp/awx.log", "a").write("change: file value not set\n")
self.changed = True
self.write_file = True
#import epdb; epdb.serve()
elif self.file_values[thisname] != self.args['value']:
open("/tmp/awx.log", "a").write("change: file value wrong -- %s != %s\n" % (self.file_value, self.args['value']))
self.changed = True
self.write_file = True
if self.proc_value is None:
open("/tmp/awx.log", "a").write("change: proc value not set\n")
self.changed = True
elif self.proc_value != self.args['value']:
open("/tmp/awx.log", "a").write("change: proc value wrong\n")
self.changed = True
self.set_proc = True
# Do the work
if not self.module.check_mode:
if self.write_file:
self.write_sysctl()
if self.write_file and self.args['reload']:
self.reload_sysctl()
if self.set_proc and self.args['sysctl_set']:
self.set_token_value(self.args['name'], self.args['value'])
# ==============================================================
# SYSCTL COMMAND MANAGEMENT
# ==============================================================
# Use the sysctl command to find the current value
def get_token_curr_value(self, token):
#sysctl_cmd = module.get_bin_path('sysctl', required=True)
rc,out,err = self.module.run_command([self.sysctl_cmd, ' -e -n ', token])
if rc != 0:
return None
else:
return shlex.split(out)[-1]
# Use the sysctl command to set the current value
def set_token_value(self, token, value):
#sysctl_cmd = module.get_bin_path('sysctl', required=True)
rc,out,err = self.module.run_command([self.sysctl_cmd, ' -w ', token, "=", value])
if rc != 0:
module.fail_json(msg='setting %s failed: %s' (token, out + err))
else:
return rc
# ==============================================================
# SYSCTL FILE MANAGEMENT
# ==============================================================
# Get the token value from the sysctl file
def read_sysctl_file(self):
#import epdb; epdb.serve()
lines = open(self.sysctl_file, "r").readlines()
for line in lines:
line = line.strip()
self.file_lines.append(line)
#open("/tmp/awx.log", "a").write("line: %s\n" % line)
#line = line.strip()
# don't split empty lines or comments
if not line or line.startswith("#"):
continue
open("/tmp/awx.log", "a").write("line: %s\n" % line)
k, v = line.split('=',1)
k = k.strip()
v = v.strip()
"""
try:
k, v = line.split('=',1)
except ValueError:
open("/tmp/awx.log", "a").write("line-error: %s\n" % line)
#import epdb; epdb.serve()
sys.exit(1)
"""
self.file_values[k] = v.strip()
# Fix the value in the sysctl file content
def fix_lines(self):
checked = []
self.fixed_lines = []
for line in self.file_lines:
if not line.strip() or line.strip().startswith("#"):
self.fixed_lines.append(line)
continue
tmpline = line.strip()
k, v = line.split('=',1)
k = k.strip()
v = v.strip()
if k not in checked:
checked.append(k)
if k == self.args['name']:
if self.args['state'] == "present":
new_line = "%s = %s\n" % (k, self.args['value'])
self.fixed_lines.append(new_line)
else:
new_line = "%s = %s\n" % (k, v)
self.fixed_lines.append(new_line)
if self.args['name'] not in checked and self.args['state'] == "present":
new_line = "%s = %s\n" % (self.args['name'], self.args['value'])
self.fixed_lines.append(new_line)
import pprint; pprint.pprint(self.fixed_lines, open("/tmp/awx.log", "a"))
#import epdb; epdb.st()
# Run sysctl -p
def reload_sysctl(self):
# do it
if get_platform().lower() == 'freebsd':
# freebsd doesn't support -p, so reload the sysctl service
rc,out,err = self.module.run_command('/etc/rc.d/sysctl reload')
else:
# system supports reloading via the -p flag to sysctl, so we'll use that
#sysctl_cmd = module.get_bin_path('sysctl', required=True)
rc,out,err = self.module.run_command([self.sysctl_cmd, '-p', self.sysctl_file])
return rc,out+err
# Completely rewrite the sysctl file
def write_sysctl(self):
# open a tmp file
fd, tmp_path = tempfile.mkstemp('.conf', '.ansible_m_sysctl_', os.path.dirname(self.sysctl_file))
f = open(tmp_path,"w")
try:
for l in self.fixed_lines:
f.write(l)
except IOError, e:
module.fail_json(msg="Failed to write to file %s: %s" % (tmp_path, str(e)))
f.flush()
f.close()
# replace the real one
self.module.atomic_move(tmp_path, self.sysctl_file)
# end
#return sysctl_args
# ==============================================================
# main
def main():
# defining module
module = AnsibleModule(
argument_spec = dict(
name = dict(aliases=['key'], required=True),
value = dict(aliases=['val'], required=False),
state = dict(default='present', choices=['present', 'absent']),
checks = dict(default='both', choices=['none', 'before', 'after', 'both']),
reload = dict(default=True, type='bool'),
sysctl_set = dict(default=True, type='bool'),
sysctl_file = dict(default='/etc/sysctl.conf')
),
supports_check_mode=True
)
result = SysctlModule(module)
module.exit_json(changed=result.changed)
sys.exit(0)
# import module snippets
from ansible.module_utils.basic import *
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment