1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3
4# Copyright: (c) 2017, Joris Weijters <joris.weijters@gmail.com>
5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7from __future__ import absolute_import, division, print_function
8__metaclass__ = type
9
10DOCUMENTATION = r'''
11---
12author:
13- Joris Weijters (@molekuul)
14module: aix_inittab
15short_description: Manages the inittab on AIX
16description:
17    - Manages the inittab on AIX.
18options:
19  name:
20    description:
21    - Name of the inittab entry.
22    type: str
23    required: yes
24    aliases: [ service ]
25  runlevel:
26    description:
27    - Runlevel of the entry.
28    type: str
29    required: yes
30  action:
31    description:
32    - Action what the init has to do with this entry.
33    type: str
34    choices:
35    - boot
36    - bootwait
37    - hold
38    - initdefault
39    - 'off'
40    - once
41    - ondemand
42    - powerfail
43    - powerwait
44    - respawn
45    - sysinit
46    - wait
47  command:
48    description:
49    - What command has to run.
50    type: str
51    required: yes
52  insertafter:
53    description:
54    - After which inittabline should the new entry inserted.
55    type: str
56  state:
57    description:
58    - Whether the entry should be present or absent in the inittab file.
59    type: str
60    choices: [ absent, present ]
61    default: present
62notes:
63  - The changes are persistent across reboots.
64  - You need root rights to read or adjust the inittab with the C(lsitab), C(chitab), C(mkitab) or C(rmitab) commands.
65  - Tested on AIX 7.1.
66requirements:
67- itertools
68'''
69
70EXAMPLES = '''
71# Add service startmyservice to the inittab, directly after service existingservice.
72- name: Add startmyservice to inittab
73  community.general.aix_inittab:
74    name: startmyservice
75    runlevel: 4
76    action: once
77    command: echo hello
78    insertafter: existingservice
79    state: present
80  become: yes
81
82# Change inittab entry startmyservice to runlevel "2" and processaction "wait".
83- name: Change startmyservice to inittab
84  community.general.aix_inittab:
85    name: startmyservice
86    runlevel: 2
87    action: wait
88    command: echo hello
89    state: present
90  become: yes
91
92- name: Remove startmyservice from inittab
93  community.general.aix_inittab:
94    name: startmyservice
95    runlevel: 2
96    action: wait
97    command: echo hello
98    state: absent
99  become: yes
100'''
101
102RETURN = '''
103name:
104    description: Name of the adjusted inittab entry
105    returned: always
106    type: str
107    sample: startmyservice
108msg:
109    description: Action done with the inittab entry
110    returned: changed
111    type: str
112    sample: changed inittab entry startmyservice
113changed:
114    description: Whether the inittab changed or not
115    returned: always
116    type: bool
117    sample: true
118'''
119
120# Import necessary libraries
121try:
122    # python 2
123    from itertools import izip
124except ImportError:
125    izip = zip
126
127from ansible.module_utils.basic import AnsibleModule
128
129# end import modules
130# start defining the functions
131
132
133def check_current_entry(module):
134    # Check if entry exists, if not return False in exists in return dict,
135    # if true return True and the entry in return dict
136    existsdict = {'exist': False}
137    lsitab = module.get_bin_path('lsitab')
138    (rc, out, err) = module.run_command([lsitab, module.params['name']])
139    if rc == 0:
140        keys = ('name', 'runlevel', 'action', 'command')
141        values = out.split(":")
142        # strip non readable characters as \n
143        values = map(lambda s: s.strip(), values)
144        existsdict = dict(izip(keys, values))
145        existsdict.update({'exist': True})
146    return existsdict
147
148
149def main():
150    # initialize
151    module = AnsibleModule(
152        argument_spec=dict(
153            name=dict(type='str', required=True, aliases=['service']),
154            runlevel=dict(type='str', required=True),
155            action=dict(type='str', choices=[
156                'boot',
157                'bootwait',
158                'hold',
159                'initdefault',
160                'off',
161                'once',
162                'ondemand',
163                'powerfail',
164                'powerwait',
165                'respawn',
166                'sysinit',
167                'wait',
168            ]),
169            command=dict(type='str', required=True),
170            insertafter=dict(type='str'),
171            state=dict(type='str', default='present', choices=['absent', 'present']),
172        ),
173        supports_check_mode=True,
174    )
175
176    result = {
177        'name': module.params['name'],
178        'changed': False,
179        'msg': ""
180    }
181
182    # Find commandline strings
183    mkitab = module.get_bin_path('mkitab')
184    rmitab = module.get_bin_path('rmitab')
185    chitab = module.get_bin_path('chitab')
186    rc = 0
187
188    # check if the new entry exists
189    current_entry = check_current_entry(module)
190
191    # if action is install or change,
192    if module.params['state'] == 'present':
193
194        # create new entry string
195        new_entry = module.params['name'] + ":" + module.params['runlevel'] + \
196            ":" + module.params['action'] + ":" + module.params['command']
197
198        # If current entry exists or fields are different(if the entry does not
199        # exists, then the entry wil be created
200        if (not current_entry['exist']) or (
201                module.params['runlevel'] != current_entry['runlevel'] or
202                module.params['action'] != current_entry['action'] or
203                module.params['command'] != current_entry['command']):
204
205            # If the entry does exist then change the entry
206            if current_entry['exist']:
207                if not module.check_mode:
208                    (rc, out, err) = module.run_command([chitab, new_entry])
209                if rc != 0:
210                    module.fail_json(
211                        msg="could not change inittab", rc=rc, err=err)
212                result['msg'] = "changed inittab entry" + " " + current_entry['name']
213                result['changed'] = True
214
215            # If the entry does not exist create the entry
216            elif not current_entry['exist']:
217                if module.params['insertafter']:
218                    if not module.check_mode:
219                        (rc, out, err) = module.run_command(
220                            [mkitab, '-i', module.params['insertafter'], new_entry])
221                else:
222                    if not module.check_mode:
223                        (rc, out, err) = module.run_command(
224                            [mkitab, new_entry])
225
226                if rc != 0:
227                    module.fail_json(msg="could not adjust inittab", rc=rc, err=err)
228                result['msg'] = "add inittab entry" + " " + module.params['name']
229                result['changed'] = True
230
231    elif module.params['state'] == 'absent':
232        # If the action is remove and the entry exists then remove the entry
233        if current_entry['exist']:
234            if not module.check_mode:
235                (rc, out, err) = module.run_command(
236                    [rmitab, module.params['name']])
237                if rc != 0:
238                    module.fail_json(
239                        msg="could not remove entry from inittab)", rc=rc, err=err)
240            result['msg'] = "removed inittab entry" + " " + current_entry['name']
241            result['changed'] = True
242
243    module.exit_json(**result)
244
245
246if __name__ == '__main__':
247    main()
248