Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
ansible
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
ansible
Commits
ca055844
Commit
ca055844
authored
Jan 13, 2014
by
James Tanner
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixes #5469 Refactor sysctl module into object oriented code,
and add a sysctl_set parameter to manage the values in /proc
parent
3a83cf1d
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
182 additions
and
243 deletions
+182
-243
library/system/sysctl
+182
-243
No files found.
library/system/sysctl
View file @
ca055844
...
...
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
# (c) 2012, David "DaviXX" CHANIAL <david.chanial@gmail.com>
# (c) 2014, James Tanner <tanner.jc@gmail.com>
#
# This file is part of Ansible
#
...
...
@@ -41,19 +42,9 @@ options:
aliases: [ 'val' ]
state:
description:
- Whether the entry should be present or absent.
- Whether the entry should be present or absent
in the sysctl file
.
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
...
...
@@ -66,6 +57,13 @@ options:
- Specifies the absolute path to C(sysctl.conf), if not C(/etc/sysctl.conf).
required: false
default: /etc/sysctl.conf
sysctl_set:
description:
- Verify token value with the sysctl command and set with -w if necessary
choices: [ "yes", "no" ]
required: false
version_added: 1.5
default: False
notes: []
requirements: []
author: David "DaviXX" CHANIAL <david.chanial@gmail.com>
...
...
@@ -78,10 +76,14 @@ EXAMPLES = '''
# 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
# Set kernel.panic to 3 in /tmp/test_sysctl.conf
- sysctl: name=kernel.panic value=3 sysctl_file=/tmp/test_sysctl.conf reload=no
# Set ip fowarding on in /proc and do not reload the sysctl file
- sysctl: name="net.ipv4.ip_forward" value=1 sysctl_set=yes
# Set ip forwarding on in /proc and in the sysctl file and reload if necessary
- sysctl: name="net.ipv4.ip_forward" value=1 sysctl_set=yes state=present reload=yes
'''
# ==============================================================
...
...
@@ -90,137 +92,168 @@ import os
import
tempfile
import
re
# ==============================================================
def
reload_sysctl
(
module
,
**
sysctl_args
):
# update needed ?
if
not
sysctl_args
[
'reload'
]:
return
0
,
''
# do it
if
get_platform
()
.
lower
()
==
'freebsd'
:
# freebsd doesn't support -p, so reload the sysctl service
rc
,
out
,
err
=
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
=
module
.
run_command
([
sysctl_cmd
,
'-p'
,
sysctl_args
[
'sysctl_file'
]])
return
rc
,
out
+
err
# ==============================================================
def
write_sysctl
(
module
,
lines
,
**
sysctl_args
):
# open a tmp file
fd
,
tmp_path
=
tempfile
.
mkstemp
(
'.conf'
,
'.ansible_m_sysctl_'
,
os
.
path
.
dirname
(
sysctl_args
[
'sysctl_file'
]))
f
=
open
(
tmp_path
,
"w"
)
try
:
for
l
in
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
module
.
atomic_move
(
tmp_path
,
sysctl_args
[
'sysctl_file'
])
# end
return
sysctl_args
# ==============================================================
def
sysctl_args_expand
(
**
sysctl_args
):
if
get_platform
()
.
lower
()
==
'freebsd'
:
# FreeBSD does not use the /proc file system, and instead
# just uses the sysctl command to set the values
sysctl_args
[
'key_path'
]
=
None
else
:
sysctl_args
[
'key_path'
]
=
sysctl_args
[
'name'
]
.
replace
(
'.'
,
'/'
)
sysctl_args
[
'key_path'
]
=
'/proc/sys/'
+
sysctl_args
[
'key_path'
]
return
sysctl_args
# ==============================================================
def
sysctl_args_collapse
(
**
sysctl_args
):
# go ahead
if
sysctl_args
.
get
(
'key_path'
)
is
not
None
:
del
sysctl_args
[
'key_path'
]
if
sysctl_args
[
'state'
]
==
'absent'
and
'value'
in
sysctl_args
:
del
sysctl_args
[
'value'
]
# end
return
sysctl_args
# ==============================================================
def
sysctl_check
(
module
,
current_step
,
**
sysctl_args
):
# no smart checks at this step ?
if
sysctl_args
[
'checks'
]
==
'none'
:
return
0
,
''
if
current_step
==
'before'
and
sysctl_args
[
'checks'
]
not
in
[
'before'
,
'both'
]:
return
0
,
''
if
current_step
==
'after'
and
sysctl_args
[
'checks'
]
not
in
[
'after'
,
'both'
]:
return
0
,
''
# checking coherence
if
sysctl_args
[
'state'
]
==
'absent'
and
sysctl_args
[
'value'
]
is
not
None
:
return
1
,
'value=x must not be supplied when state=absent'
if
sysctl_args
[
'state'
]
==
'present'
and
sysctl_args
[
'value'
]
is
None
:
return
1
,
'value=x must be supplied when state=present'
if
not
sysctl_args
[
'reload'
]
and
sysctl_args
[
'checks'
]
in
[
'after'
,
'both'
]:
return
1
,
'checks cannot be set to after or both if reload=no'
if
sysctl_args
[
'key_path'
]
is
not
None
:
# getting file stat
if
not
os
.
access
(
sysctl_args
[
'key_path'
],
os
.
F_OK
):
return
1
,
'key_path is not an existing file, key
%
s seems invalid'
%
sysctl_args
[
'key_path'
]
if
not
os
.
access
(
sysctl_args
[
'key_path'
],
os
.
R_OK
):
return
1
,
'key_path is not a readable file, key seems to be uncheckable'
# checks before
if
current_step
==
'before'
and
sysctl_args
[
'checks'
]
in
[
'before'
,
'both'
]:
if
sysctl_args
[
'key_path'
]
is
not
None
and
not
os
.
access
(
sysctl_args
[
'key_path'
],
os
.
W_OK
):
return
1
,
'key_path is not a writable file, key seems to be read only'
return
0
,
''
# checks after
if
current_step
==
'after'
and
sysctl_args
[
'checks'
]
in
[
'after'
,
'both'
]:
if
sysctl_args
[
'value'
]
is
not
None
:
if
sysctl_args
[
'key_path'
]
is
not
None
:
# reading the virtual file
f
=
open
(
sysctl_args
[
'key_path'
],
'r'
)
output
=
f
.
read
()
f
.
close
()
else
:
# we're on a system without /proc (ie. freebsd), so just
# use the sysctl command to get the currently set value
sysctl_cmd
=
module
.
get_bin_path
(
'sysctl'
,
required
=
True
)
rc
,
output
,
stderr
=
module
.
run_command
(
"
%
s -n
%
s"
%
(
sysctl_cmd
,
sysctl_args
[
'name'
]))
if
rc
!=
0
:
return
1
,
'failed to lookup the value via the sysctl command'
output
=
output
.
strip
(
'
\t\n\r
'
)
output
=
re
.
sub
(
r'\s+'
,
' '
,
output
)
# normal case, found value must be equal to the submitted value, and
# we compare the exploded values to handle any whitepsace differences
if
output
.
split
()
!=
sysctl_args
[
'value'
]
.
split
():
return
1
,
'key seems not set to value even after update/sysctl, founded : <
%
s>, wanted : <
%
s>'
%
(
output
,
sysctl_args
[
'value'
])
return
0
,
''
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
):
# Whitespace is bad
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
# update file contents with desired token/value
self
.
fix_lines
()
# what do we need to do now?
if
self
.
file_values
[
thisname
]
is
None
and
self
.
args
[
'state'
]
==
"present"
:
self
.
changed
=
True
self
.
write_file
=
True
elif
self
.
file_values
[
thisname
]
!=
self
.
args
[
'value'
]:
self
.
changed
=
True
self
.
write_file
=
True
if
self
.
args
[
'sysctl_set'
]:
if
self
.
proc_value
is
None
:
self
.
changed
=
True
elif
self
.
proc_value
!=
self
.
args
[
'value'
]:
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
:
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
):
thiscmd
=
"
%
s -e -n
%
s"
%
(
self
.
sysctl_cmd
,
token
)
rc
,
out
,
err
=
self
.
module
.
run_command
(
thiscmd
)
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
):
thiscmd
=
"
%
s -w
%
s=
%
s"
%
(
self
.
sysctl_cmd
,
token
,
value
)
rc
,
out
,
err
=
self
.
module
.
run_command
(
thiscmd
)
if
rc
!=
0
:
self
.
module
.
fail_json
(
msg
=
'setting
%
s failed:
%
s'
(
token
,
out
+
err
))
else
:
return
rc
# 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
:
# no value was supplied, so we're checking to make sure
# the associated name is absent. We just fudge this since
# the sysctl isn't really gone, just removed from the conf
# file meaning it will be whatever the system default is
return
0
,
''
# system supports reloading via the -p flag to sysctl, so we'll use that
rc
,
out
,
err
=
self
.
module
.
run_command
([
self
.
sysctl_cmd
,
'-p'
,
self
.
sysctl_file
])
return
rc
,
out
+
err
# ==============================================================
# SYSCTL FILE MANAGEMENT
# ==============================================================
# Get the token value from the sysctl file
def
read_sysctl_file
(
self
):
lines
=
open
(
self
.
sysctl_file
,
"r"
)
.
readlines
()
for
line
in
lines
:
line
=
line
.
strip
()
self
.
file_lines
.
append
(
line
)
# don't split empty lines or comments
if
not
line
or
line
.
startswith
(
"#"
):
continue
k
,
v
=
line
.
split
(
'='
,
1
)
k
=
k
.
strip
()
v
=
v
.
strip
()
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
)
# 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
:
self
.
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
)
# weird end
return
1
,
'unexpected position reached'
# ==============================================================
# main
...
...
@@ -233,110 +266,16 @@ def main():
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
)
# defaults
sysctl_args
=
{
'changed'
:
False
,
'name'
:
module
.
params
[
'name'
],
'state'
:
module
.
params
[
'state'
],
'checks'
:
module
.
params
[
'checks'
],
'reload'
:
module
.
params
[
'reload'
],
'value'
:
module
.
params
.
get
(
'value'
),
'sysctl_file'
:
module
.
params
[
'sysctl_file'
]
}
# prepare vars
sysctl_args
=
sysctl_args_expand
(
**
sysctl_args
)
if
get_platform
()
.
lower
()
==
'freebsd'
:
# freebsd does not like spaces around the equal sign
pattern
=
"
%
s=
%
s
\n
"
else
:
pattern
=
"
%
s =
%
s
\n
"
new_line
=
pattern
%
(
sysctl_args
[
'name'
],
sysctl_args
[
'value'
])
to_write
=
[]
founded
=
False
# make checks before act
res
,
msg
=
sysctl_check
(
module
,
'before'
,
**
sysctl_args
)
if
res
!=
0
:
module
.
fail_json
(
msg
=
'checks_before failed with: '
+
msg
)
if
not
os
.
access
(
sysctl_args
[
'sysctl_file'
],
os
.
W_OK
):
try
:
f
=
open
(
sysctl_args
[
'sysctl_file'
],
'w'
)
f
.
close
()
except
IOError
,
e
:
module
.
fail_json
(
msg
=
'unable to create supplied sysctl file (destination directory probably missing)'
)
# reading the file
for
line
in
open
(
sysctl_args
[
'sysctl_file'
],
'r'
)
.
readlines
():
if
not
line
.
strip
():
to_write
.
append
(
line
)
continue
if
line
.
strip
()
.
startswith
(
'#'
):
to_write
.
append
(
line
)
continue
# write line if not the one searched
ld
=
{}
ld
[
'name'
],
ld
[
'val'
]
=
line
.
split
(
'='
,
1
)
ld
[
'name'
]
=
ld
[
'name'
]
.
strip
()
if
ld
[
'name'
]
!=
sysctl_args
[
'name'
]:
to_write
.
append
(
line
)
continue
# should be absent ?
if
sysctl_args
[
'state'
]
==
'absent'
:
# not writing the founded line
# mark as changed
sysctl_args
[
'changed'
]
=
True
# should be present
if
sysctl_args
[
'state'
]
==
'present'
:
# is the founded line equal to the wanted one ?
ld
[
'val'
]
=
ld
[
'val'
]
.
strip
()
if
ld
[
'val'
]
==
sysctl_args
[
'value'
]:
# line is equal, writing it without update (but cancel repeats)
if
sysctl_args
[
'changed'
]
==
False
and
founded
==
False
:
to_write
.
append
(
line
)
founded
=
True
else
:
# update the line (but cancel repeats)
if
sysctl_args
[
'changed'
]
==
False
and
founded
==
False
:
to_write
.
append
(
new_line
)
sysctl_args
[
'changed'
]
=
True
continue
result
=
SysctlModule
(
module
)
# if not changed, but should be present, so we have to add it
if
sysctl_args
[
'state'
]
==
'present'
and
sysctl_args
[
'changed'
]
==
False
and
founded
==
False
:
to_write
.
append
(
new_line
)
sysctl_args
[
'changed'
]
=
True
# has changed ?
res
=
0
if
sysctl_args
[
'changed'
]
==
True
:
sysctl_args
=
write_sysctl
(
module
,
to_write
,
**
sysctl_args
)
res
,
msg
=
reload_sysctl
(
module
,
**
sysctl_args
)
# make checks after act
res
,
msg
=
sysctl_check
(
module
,
'after'
,
**
sysctl_args
)
if
res
!=
0
:
module
.
fail_json
(
msg
=
'checks_after failed with: '
+
msg
)
# look at the next link to avoid this workaround
# https://groups.google.com/forum/?fromgroups=#!topic/ansible-project/LMY-dwF6SQk
changed
=
sysctl_args
[
'changed'
]
del
sysctl_args
[
'changed'
]
# end
sysctl_args
=
sysctl_args_collapse
(
**
sysctl_args
)
module
.
exit_json
(
changed
=
changed
,
**
sysctl_args
)
module
.
exit_json
(
changed
=
result
.
changed
)
sys
.
exit
(
0
)
# import module snippets
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment