@ -19,11 +19,6 @@
# You should have received a copy of the GNU General Public License
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import re
import os
import pipes
import tempfile
DOCUMENTATION = """
DOCUMENTATION = """
- - -
- - -
module : lineinfile
module : lineinfile
@ -146,11 +141,21 @@ EXAMPLES = r"""
- lineinfile : dest = / etc / sudoers state = present regexp = ' ^ % ADMIN ALL \ = ' line = ' % ADMIN ALL=(ALL) NOPASSWD:ALL ' validate = ' visudo -cf %s '
- lineinfile : dest = / etc / sudoers state = present regexp = ' ^ % ADMIN ALL \ = ' line = ' % ADMIN ALL=(ALL) NOPASSWD:ALL ' validate = ' visudo -cf %s '
"""
"""
def write_changes ( module , lines , dest ) :
import re
import os
import tempfile
# import module snippets
from ansible . module_utils . basic import AnsibleModule
from ansible . module_utils . six import b
from ansible . module_utils . _text import to_bytes , to_native
def write_changes ( module , b_lines , dest ) :
tmpfd , tmpfile = tempfile . mkstemp ( )
tmpfd , tmpfile = tempfile . mkstemp ( )
f = os . fdopen ( tmpfd , ' wb ' )
f = os . fdopen ( tmpfd , ' wb ' )
f . writelines ( lines )
f . writelines ( b_ lines)
f . close ( )
f . close ( )
validate = module . params . get ( ' validate ' , None )
validate = module . params . get ( ' validate ' , None )
@ -158,14 +163,15 @@ def write_changes(module,lines,dest):
if validate :
if validate :
if " %s " not in validate :
if " %s " not in validate :
module . fail_json ( msg = " validate must contain %% s: %s " % ( validate ) )
module . fail_json ( msg = " validate must contain %% s: %s " % ( validate ) )
( rc , out , err ) = module . run_command ( validate % tmpfile )
( rc , out , err ) = module . run_command ( to_bytes( validate % tmpfile ) )
valid = rc == 0
valid = rc == 0
if rc != 0 :
if rc != 0 :
module . fail_json ( msg = ' failed to validate: '
module . fail_json ( msg = ' failed to validate: '
' rc: %s error: %s ' % ( rc , err ) )
' rc: %s error: %s ' % ( rc , err ) )
if valid :
if valid :
module . atomic_move ( tmpfile , os . path . realpath ( dest ) , unsafe_writes = module . params [ ' unsafe_writes ' ] )
module . atomic_move ( tmpfile , os . path . realpath ( dest ) , unsafe_writes = module . params [ ' unsafe_writes ' ] )
def check_file_attrs ( module , changed , message , diff ) :
def check_file_attrs ( module , changed , message , diff ) :
file_args = module . load_file_common_arguments ( module . params )
file_args = module . load_file_common_arguments ( module . params )
@ -187,44 +193,46 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create,
' before_header ' : ' %s (content) ' % dest ,
' before_header ' : ' %s (content) ' % dest ,
' after_header ' : ' %s (content) ' % dest }
' after_header ' : ' %s (content) ' % dest }
if not os . path . exists ( dest ) :
b_dest = to_bytes ( dest )
if not os . path . exists ( b_dest ) :
if not create :
if not create :
module . fail_json ( rc = 257 , msg = ' Destination %s does not exist ! ' % dest )
module . fail_json ( rc = 257 , msg = ' Destination %s does not exist ! ' % dest )
destpath = os . path . dirname ( dest)
b_ destpath = os . path . dirname ( b_ dest)
if not os . path . exists ( destpath) and not module . check_mode :
if not os . path . exists ( b_ destpath) and not module . check_mode :
os . makedirs ( destpath)
os . makedirs ( b_ destpath)
lines = [ ]
b_ lines = [ ]
else :
else :
f = open ( dest, ' rb ' )
f = open ( b_ dest, ' rb ' )
lines = f . readlines ( )
b_ lines = f . readlines ( )
f . close ( )
f . close ( )
if module . _diff :
if module . _diff :
diff [ ' before ' ] = ' ' . join ( lines)
diff [ ' before ' ] = to_native ( b ( ' ' ) . join ( b_ lines) )
if regexp is not None :
if regexp is not None :
mre = re . compile ( regexp)
bre_m = re . compile ( to_bytes( regexp) )
if insertafter not in ( None , ' BOF ' , ' EOF ' ) :
if insertafter not in ( None , ' BOF ' , ' EOF ' ) :
insre = re . compile ( insertafter)
bre_ ins = re . compile ( to_bytes( insertafter) )
elif insertbefore not in ( None , ' BOF ' ) :
elif insertbefore not in ( None , ' BOF ' ) :
insre = re . compile ( insertbefore)
bre_ ins = re . compile ( to_bytes( insertbefore) )
else :
else :
insre = None
bre_ ins = None
# index[0] is the line num where regexp has been found
# index[0] is the line num where regexp has been found
# index[1] is the line num where insertafter/inserbefore has been found
# index[1] is the line num where insertafter/inserbefore has been found
index = [ - 1 , - 1 ]
index = [ - 1 , - 1 ]
m = None
m = None
for lineno , cur_line in enumerate ( lines ) :
b_line = to_bytes ( line )
for lineno , b_cur_line in enumerate ( b_lines ) :
if regexp is not None :
if regexp is not None :
match_found = mre. search ( cur_line)
match_found = bre_m. search ( b_ cur_line)
else :
else :
match_found = line == cur_line. rstrip ( ' \r \n ' )
match_found = b_ line == b_ cur_line. rstrip ( b ( ' \r \n ' ) )
if match_found :
if match_found :
index [ 0 ] = lineno
index [ 0 ] = lineno
m = match_found
m = match_found
elif insre is not None and insre . search ( cur_line) :
elif bre_ ins is not None and bre_ ins. search ( b_ cur_line) :
if insertafter :
if insertafter :
# + 1 for the next line
# + 1 for the next line
index [ 1 ] = lineno + 1
index [ 1 ] = lineno + 1
@ -235,18 +243,19 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create,
msg = ' '
msg = ' '
changed = False
changed = False
# Regexp matched a line in the file
# Regexp matched a line in the file
b_linesep = to_bytes ( os . linesep )
if index [ 0 ] != - 1 :
if index [ 0 ] != - 1 :
if backrefs :
if backrefs :
new_line = m . expand ( line)
b_ new_line = m . expand ( b_ line)
else :
else :
# Don't do backref expansion if not asked.
# Don't do backref expansion if not asked.
new_line = line
b_ new_line = b_ line
if not new_line. endswith ( os. linesep) :
if not b_ new_line. endswith ( b_ linesep) :
new_line + = os . linesep
b_new_line + = b_ linesep
if lines[ index [ 0 ] ] != new_line:
if b_ lines[ index [ 0 ] ] != b_ new_line:
lines[ index [ 0 ] ] = new_line
b_ lines[ index [ 0 ] ] = b_ new_line
msg = ' line replaced '
msg = ' line replaced '
changed = True
changed = True
elif backrefs :
elif backrefs :
@ -255,7 +264,7 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create,
pass
pass
# Add it to the beginning of the file
# Add it to the beginning of the file
elif insertbefore == ' BOF ' or insertafter == ' BOF ' :
elif insertbefore == ' BOF ' or insertafter == ' BOF ' :
lines. insert ( 0 , line + os . linesep)
b_ lines. insert ( 0 , b_line + b_ linesep)
msg = ' line added '
msg = ' line added '
changed = True
changed = True
# Add it to the end of the file if requested or
# Add it to the end of the file if requested or
@ -264,28 +273,28 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create,
elif insertafter == ' EOF ' or index [ 1 ] == - 1 :
elif insertafter == ' EOF ' or index [ 1 ] == - 1 :
# If the file is not empty then ensure there's a newline before the added line
# If the file is not empty then ensure there's a newline before the added line
if len ( lines) > 0 and not ( lines [ - 1 ] . endswith ( ' \n ' ) or lines [ - 1 ] . endswith ( ' \r ' ) ) :
if len ( b_lines) > 0 and not b_lines [ - 1 ] [ - 1 : ] in ( b ( ' \n ' ) , b ( ' \r ' ) ) :
lines. append ( os . linesep)
b_lines. append ( b_ linesep)
lines. append ( line + os . linesep)
b_lines. append ( b_line + b_ linesep)
msg = ' line added '
msg = ' line added '
changed = True
changed = True
# insert* matched, but not the regexp
# insert* matched, but not the regexp
else :
else :
lines. insert ( index [ 1 ] , line + os . linesep)
b_ lines. insert ( index [ 1 ] , b_line + b_ linesep)
msg = ' line added '
msg = ' line added '
changed = True
changed = True
if module . _diff :
if module . _diff :
diff [ ' after ' ] = ' ' . join ( lines)
diff [ ' after ' ] = to_native ( b ( ' ' ) . join ( b_ lines) )
backupdest = " "
backupdest = " "
if changed and not module . check_mode :
if changed and not module . check_mode :
if backup and os . path . exists ( dest) :
if backup and os . path . exists ( b_ dest) :
backupdest = module . backup_local ( dest )
backupdest = module . backup_local ( dest )
write_changes ( module , lines, dest )
write_changes ( module , b_ lines, dest )
if module . check_mode and not os . path . exists ( dest) :
if module . check_mode and not os . path . exists ( b_ dest) :
module . exit_json ( changed = changed , msg = msg , backup = backupdest , diff = diff )
module . exit_json ( changed = changed , msg = msg , backup = backupdest , diff = diff )
attr_diff = { }
attr_diff = { }
@ -300,7 +309,8 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create,
def absent ( module , dest , regexp , line , backup ) :
def absent ( module , dest , regexp , line , backup ) :
if not os . path . exists ( dest ) :
b_dest = to_bytes ( dest )
if not os . path . exists ( b_dest ) :
module . exit_json ( changed = False , msg = " file not present " )
module . exit_json ( changed = False , msg = " file not present " )
msg = ' '
msg = ' '
@ -309,37 +319,38 @@ def absent(module, dest, regexp, line, backup):
' before_header ' : ' %s (content) ' % dest ,
' before_header ' : ' %s (content) ' % dest ,
' after_header ' : ' %s (content) ' % dest }
' after_header ' : ' %s (content) ' % dest }
f = open ( dest, ' rb ' )
f = open ( b_ dest, ' rb ' )
lines = f . readlines ( )
b_ lines = f . readlines ( )
f . close ( )
f . close ( )
if module . _diff :
if module . _diff :
diff [ ' before ' ] = ' ' . join ( lines)
diff [ ' before ' ] = to_native ( b ( ' ' ) . join ( b_ lines) )
if regexp is not None :
if regexp is not None :
cre = re . compile ( regexp)
bre_c = re . compile ( to_bytes( regexp) )
found = [ ]
found = [ ]
def matcher ( cur_line ) :
b_line = to_bytes ( line )
def matcher ( b_cur_line ) :
if regexp is not None :
if regexp is not None :
match_found = cre. search ( cur_line)
match_found = bre_c. search ( b_ cur_line)
else :
else :
match_found = line == cur_line. rstrip ( ' \r \n ' )
match_found = b_ line == b_ cur_line. rstrip ( b ( ' \r \n ' ) )
if match_found :
if match_found :
found . append ( cur_line)
found . append ( b_ cur_line)
return not match_found
return not match_found
lines = filter ( matcher , lines )
b_lines = [ l for l in b_lines if matcher ( l ) ]
changed = len ( found ) > 0
changed = len ( found ) > 0
if module . _diff :
if module . _diff :
diff [ ' after ' ] = ' ' . join ( lines)
diff [ ' after ' ] = to_native ( b ( ' ' ) . join ( b_ lines) )
backupdest = " "
backupdest = " "
if changed and not module . check_mode :
if changed and not module . check_mode :
if backup :
if backup :
backupdest = module . backup_local ( dest )
backupdest = module . backup_local ( dest )
write_changes ( module , lines, dest )
write_changes ( module , b_ lines, dest )
if changed :
if changed :
msg = " %s line(s) removed " % len ( found )
msg = " %s line(s) removed " % len ( found )
@ -358,7 +369,7 @@ def absent(module, dest, regexp, line, backup):
def main ( ) :
def main ( ) :
module = AnsibleModule (
module = AnsibleModule (
argument_spec = dict (
argument_spec = dict (
dest = dict ( required = True , aliases = [ ' name ' , ' destfile ' ] ),
dest = dict ( required = True , aliases = [ ' name ' , ' destfile ' ] , type = ' path ' ),
state = dict ( default = ' present ' , choices = [ ' absent ' , ' present ' ] ) ,
state = dict ( default = ' present ' , choices = [ ' absent ' , ' present ' ] ) ,
regexp = dict ( default = None ) ,
regexp = dict ( default = None ) ,
line = dict ( aliases = [ ' value ' ] ) ,
line = dict ( aliases = [ ' value ' ] ) ,
@ -375,13 +386,13 @@ def main():
)
)
params = module . params
params = module . params
create = module . params [ ' create ' ]
create = params [ ' create ' ]
backup = module . params [ ' backup ' ]
backup = params [ ' backup ' ]
backrefs = module . params [ ' backrefs ' ]
backrefs = params [ ' backrefs ' ]
dest = os . path . expanduser ( params [ ' dest ' ] )
dest = params [ ' dest ' ]
if os . path . isdir ( dest ) :
b_dest = to_bytes ( dest )
if os . path . isdir ( b_dest ) :
module . fail_json ( rc = 256 , msg = ' Destination %s is a directory ! ' % dest )
module . fail_json ( rc = 256 , msg = ' Destination %s is a directory ! ' % dest )
if params [ ' state ' ] == ' present ' :
if params [ ' state ' ] == ' present ' :
@ -407,8 +418,5 @@ def main():
absent ( module , dest , params [ ' regexp ' ] , params . get ( ' line ' , None ) , backup )
absent ( module , dest , params [ ' regexp ' ] , params . get ( ' line ' , None ) , backup )
# import module snippets
from ansible . module_utils . basic import *
from ansible . module_utils . splitter import *
if __name__ == ' __main__ ' :
if __name__ == ' __main__ ' :
main ( )
main ( )