ok here is the full magic...i am trying to build an ansible module which receives atrributes in the following formats in a dcit.
attr=val,attr=val,and so on
i need to use the key/value pairs to write them to a config file in the following format.
bla:
attr = val
attr = val
#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = '''
---
module: aix_stanza
short_description: modify aix stanza files
version_added: "0.1"
description:
- "adds stanza lines/addr/value pairs, changes attr/value pairs, removes stanzas/attr/value pairs"
options:
path:
description:
- Path to the stanza file
required: true
stanza:
description:
- name of add_stanza
required: true
options:
description:
- comman separated key/value pairs (key=val,key=val,...)
backup:
description:
- Create a backup file including the timestamp information so you can get
the original file back if you somehow clobbered it incorrectly.
type: bool
default: 'no'
state:
description:
- If set to C(absent) the stanza will be removed if present instead of created.
choices: [ absent, present ]
default: present
others:
description:
- All arguments accepted by the M(file) module also work here
extends_documentation_fragment:
- files
author:
- Christian Tremel (@flynn1973)
'''
EXAMPLES = '''
- name: add ldap user stanza
aix_stanza:
path: /etc/security/user
stanza: exampleuser
options: SYSTEM=LDAP,registry=LDAP
state: present
mode: 0644
backup: yes
- name: add filesystem entry
aix_stanza:
path: /etc/filesystems
stanza: /examplemount
options: dev=/dev/lvosystem_c,vfs=jfs2,log=INLINE,mount=true
state: present
backup: yes
'''
import os
import re
import tempfile
import traceback
from ansible.module_utils.basic import *
def match_option(option, line):
option = re.escape(option)
return re.match('( |\t)*%s( |\t)*(=|$)' % option, line) \
or re.match('#( |\t)*%s( |\t)*(=|$)' % option, line) \
or re.match(';( |\t)*%s( |\t)*(=|$)' % option, line)
def match_active_option(option, line):
option = re.escape(option)
return re.match('( |\t)*%s( |\t)*(=|$)' % option, line)
def match_stanza(stanza, line):
stanza = re.escape(stanza)
return re.match('^%s:[^:]*(?=[\s])' % stanza, line)
def do_stanza(module, filename, stanza=None, options=None, state='present', backup=False, create=True):
diff = dict(
before='',
after='',
before_header='%s (content)' % filename,
after_header='%s (content)' % filename,
)
if not os.path.exists(filename):
if not create:
module.fail_json(rc=257, msg='Destination %s does not exist !' % filename)
destpath = os.path.dirname(filename)
if not os.path.exists(destpath) and not module.check_mode:
os.makedirs(destpath)
stanza_lines = []
else:
stanza_file = open(filename, 'r')
try:
stanza_lines = stanza_file.readlines()
finally:
stanza_file.close()
if module._diff:
diff['before'] = ''.join(stanza_lines)
changed = False
# ini file could be empty
if not stanza_lines:
stanza_lines.append('\n')
# last line should always be a newline to keep up with POSIX standard
if stanza_lines[-1] == "" or stanza_lines[-1][-1] != '\n':
stanza_lines[-1] += '\n'
changed = True
if not stanza:
module.fail_json(msg="stanza name not set", rc=rc, err=err)
stanza_format = '\n%s\n'
option_format = '\t%s = %s\n'
for index, line in enumerate(stanza_lines):
if line.startswith('%s:' % stanza):
within_stanza = True
stanza_start = index
if within_stanza:
if state == 'present':
# insert missing option line at the end of the section
for i in range(index, 0, -1):
# search backwards for previous non-blank or non-comment line
if not re.match(r'^[ \t]*([#;].*)?$', stanza_lines[i - 1]):
stanza_lines.insert(i, option_format % (options, value))
msg = 'options added'
changed = True
break
elif state == 'absent' and not options:
# remove the entire stanza
del stanza_lines[stanza_start:index]
msg = 'stanza removed'
changed = True
break
else:
if within_stanza and options:
if state == 'present':
# change the existing option line
if match_option(options, line):
newline = option_format % (options, value)
option_changed = stanza_lines[index] != newline
changed = changed or option_changed
if option_changed:
msg = 'option changed'
stanza_lines[index] = newline
if option_changed:
# remove all possible option occurrences from the stanza
index = index + 1
while index < len(stanza_lines):
line = stanza_lines[index]
if match_active_option(options, line):
del stanza_lines[index]
else:
index = index + 1
break
elif state == 'absent':
# delete the existing line
if match_active_option(options, line):
del stanza_lines[index]
changed = True
msg = 'option changed'
break
if not within_stanza and option and state == 'present':
stanza_lines.append('\n%s:\n' % stanza)
stanza_lines.append(assignment_format % (options, value))
changed = True
msg = 'stanza and option added'
if module._diff:
diff['after'] = ''.join(stanza_lines)
backup_file = None
if changed and not module.check_mode:
if backup:
backup_file = module.backup_local(filename)
try:
tmpfd, tmpfile = tempfile.mkstemp(dir=module.tmpdir)
f = os.fdopen(tmpfd, 'w')
f.writelines(stanza_lines)
f.close()
except IOError:
module.fail_json(msg="Unable to create temporary file %s", traceback=traceback.format_exc())
try:
module.atomic_move(tmpfile, filename)
except IOError:
module.ansible.fail_json(msg='Unable to move temporary file %s to %s, IOError' % (tmpfile, filename), traceback=traceback.format_exc())
return (changed, backup_file, diff, msg)
def main():
module = AnsibleModule(
argument_spec=dict(
path=dict(type='path', required=True, aliases=['dest']),
stanza=dict(type='str', required=True),
options=dict(type='dict'),
backup=dict(type='bool', default=False),
state=dict(type='str', default='present', choices=['absent', 'present']),
create=dict(type='bool', default=True)
),
add_file_common_args=True,
supports_check_mode=True,
)
path = module.params['path']
stanza = module.params['stanza']
options = module.params['options']
state = module.params['state']
backup = module.params['backup']
create = module.params['create']
(changed, backup_file, diff, msg) = do_stanza(module, path, stanza, options, state, backup, create)
if not module.check_mode and os.path.exists(path):
file_args = module.load_file_common_arguments(module.params)
changed = module.set_fs_attributes_if_different(file_args, changed)
results = dict(
changed=changed,
diff=diff,
msg=msg,
path=path,
)
if backup_file is not None:
results['backup_file'] = backup_file
# Mission complete
module.exit_json(**results)
if __name__ == '__main__':
main()