summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/general/plugins/modules/aix_lvg.py
blob: 2892a68ad97c0bd415bbad63de3f808ee3115d61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (c) 2017, Kairo Araujo <kairo@kairo.eti.br>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

from __future__ import absolute_import, division, print_function
__metaclass__ = type

DOCUMENTATION = r'''
---
author:
  - Kairo Araujo (@kairoaraujo)
module: aix_lvg
short_description: Manage LVM volume groups on AIX
description:
  - This module creates, removes or resize volume groups on AIX LVM.
extends_documentation_fragment:
  - community.general.attributes
attributes:
  check_mode:
    support: full
  diff_mode:
    support: none
options:
  force:
    description:
    - Force volume group creation.
    type: bool
    default: false
  pp_size:
    description:
    - The size of the physical partition in megabytes.
    type: int
  pvs:
    description:
    - List of comma-separated devices to use as physical devices in this volume group.
    - Required when creating or extending (V(present) state) the volume group.
    - If not informed reducing (V(absent) state) the volume group will be removed.
    type: list
    elements: str
  state:
    description:
    - Control if the volume group exists and volume group AIX state varyonvg V(varyon) or varyoffvg V(varyoff).
    type: str
    choices: [ absent, present, varyoff, varyon ]
    default: present
  vg:
    description:
    - The name of the volume group.
    type: str
    required: true
  vg_type:
    description:
    - The type of the volume group.
    type: str
    choices: [ big, normal, scalable ]
    default: normal
notes:
- AIX will permit remove VG only if all LV/Filesystems are not busy.
- Module does not modify PP size for already present volume group.
'''

EXAMPLES = r'''
- name: Create a volume group datavg
  community.general.aix_lvg:
    vg: datavg
    pp_size: 128
    vg_type: scalable
    state: present

- name: Removing a volume group datavg
  community.general.aix_lvg:
    vg: datavg
    state: absent

- name: Extending rootvg
  community.general.aix_lvg:
    vg: rootvg
    pvs: hdisk1
    state: present

- name: Reducing rootvg
  community.general.aix_lvg:
    vg: rootvg
    pvs: hdisk1
    state: absent
'''

RETURN = r''' # '''

from ansible.module_utils.basic import AnsibleModule


def _validate_pv(module, vg, pvs):
    """
    Function to validate if the physical volume (PV) is not already in use by
    another volume group or Oracle ASM.

    :param module: Ansible module argument spec.
    :param vg: Volume group name.
    :param pvs: Physical volume list.
    :return: [bool, message] or module.fail_json for errors.
    """

    lspv_cmd = module.get_bin_path('lspv', True)
    rc, current_lspv, stderr = module.run_command([lspv_cmd])
    if rc != 0:
        module.fail_json(msg="Failed executing 'lspv' command.", rc=rc, stdout=current_lspv, stderr=stderr)

    for pv in pvs:
        # Get pv list.
        lspv_list = {}
        for line in current_lspv.splitlines():
            pv_data = line.split()
            lspv_list[pv_data[0]] = pv_data[2]

        # Check if pv exists and is free.
        if pv not in lspv_list.keys():
            module.fail_json(msg="Physical volume '%s' doesn't exist." % pv)

        if lspv_list[pv] == 'None':
            # Disk None, looks free.
            # Check if PV is not already in use by Oracle ASM.
            lquerypv_cmd = module.get_bin_path('lquerypv', True)
            rc, current_lquerypv, stderr = module.run_command([lquerypv_cmd, "-h", "/dev/%s" % pv, "20", "10"])
            if rc != 0:
                module.fail_json(msg="Failed executing lquerypv command.", rc=rc, stdout=current_lquerypv, stderr=stderr)

            if 'ORCLDISK' in current_lquerypv:
                module.fail_json("Physical volume '%s' is already used by Oracle ASM." % pv)

            msg = "Physical volume '%s' is ok to be used." % pv
            return True, msg

        # Check if PV is already in use for the same vg.
        elif vg != lspv_list[pv]:
            module.fail_json(msg="Physical volume '%s' is in use by another volume group '%s'." % (pv, lspv_list[pv]))

        msg = "Physical volume '%s' is already used by volume group '%s'." % (pv, lspv_list[pv])
        return False, msg


def _validate_vg(module, vg):
    """
    Check the current state of volume group.

    :param module: Ansible module argument spec.
    :param vg: Volume Group name.
    :return: True (VG in varyon state) or False (VG in varyoff state) or
             None (VG does not exist), message.
    """
    lsvg_cmd = module.get_bin_path('lsvg', True)
    rc, current_active_vgs, err = module.run_command([lsvg_cmd, "-o"])
    if rc != 0:
        module.fail_json(msg="Failed executing '%s' command." % lsvg_cmd)

    rc, current_all_vgs, err = module.run_command([lsvg_cmd])
    if rc != 0:
        module.fail_json(msg="Failed executing '%s' command." % lsvg_cmd)

    if vg in current_all_vgs and vg not in current_active_vgs:
        msg = "Volume group '%s' is in varyoff state." % vg
        return False, msg

    if vg in current_active_vgs:
        msg = "Volume group '%s' is in varyon state." % vg
        return True, msg

    msg = "Volume group '%s' does not exist." % vg
    return None, msg


def create_extend_vg(module, vg, pvs, pp_size, vg_type, force, vg_validation):
    """ Creates or extend a volume group. """

    # Command option parameters.
    force_opt = {
        True: '-f',
        False: ''
    }

    vg_opt = {
        'normal': '',
        'big': '-B',
        'scalable': '-S',
    }

    # Validate if PV are not already in use.
    pv_state, msg = _validate_pv(module, vg, pvs)
    if not pv_state:
        changed = False
        return changed, msg

    vg_state, msg = vg_validation
    if vg_state is False:
        changed = False
        return changed, msg

    elif vg_state is True:
        # Volume group extension.
        changed = True
        msg = ""

        if not module.check_mode:
            extendvg_cmd = module.get_bin_path('extendvg', True)
            rc, output, err = module.run_command([extendvg_cmd, vg] + pvs)
            if rc != 0:
                changed = False
                msg = "Extending volume group '%s' has failed." % vg
                return changed, msg

        msg = "Volume group '%s' extended." % vg
        return changed, msg

    elif vg_state is None:
        # Volume group creation.
        changed = True
        msg = ''

        if not module.check_mode:
            mkvg_cmd = module.get_bin_path('mkvg', True)
            rc, output, err = module.run_command([mkvg_cmd, vg_opt[vg_type], pp_size, force_opt[force], "-y", vg] + pvs)
            if rc != 0:
                changed = False
                msg = "Creating volume group '%s' failed." % vg
                return changed, msg

        msg = "Volume group '%s' created." % vg
        return changed, msg


def reduce_vg(module, vg, pvs, vg_validation):
    vg_state, msg = vg_validation

    if vg_state is False:
        changed = False
        return changed, msg

    elif vg_state is None:
        changed = False
        return changed, msg

    # Define pvs_to_remove (list of physical volumes to be removed).
    if pvs is None:
        # Remove VG if pvs are note informed.
        # Remark: AIX will permit remove only if the VG has not LVs.
        lsvg_cmd = module.get_bin_path('lsvg', True)
        rc, current_pvs, err = module.run_command([lsvg_cmd, "-p", vg])
        if rc != 0:
            module.fail_json(msg="Failing to execute '%s' command." % lsvg_cmd)

        pvs_to_remove = []
        for line in current_pvs.splitlines()[2:]:
            pvs_to_remove.append(line.split()[0])

        reduce_msg = "Volume group '%s' removed." % vg
    else:
        pvs_to_remove = pvs
        reduce_msg = ("Physical volume(s) '%s' removed from Volume group '%s'." % (' '.join(pvs_to_remove), vg))

    # Reduce volume group.
    if len(pvs_to_remove) <= 0:
        changed = False
        msg = "No physical volumes to remove."
        return changed, msg

    changed = True
    msg = ''

    if not module.check_mode:
        reducevg_cmd = module.get_bin_path('reducevg', True)
        rc, stdout, stderr = module.run_command([reducevg_cmd, "-df", vg] + pvs_to_remove)
        if rc != 0:
            module.fail_json(msg="Unable to remove '%s'." % vg, rc=rc, stdout=stdout, stderr=stderr)

    msg = reduce_msg
    return changed, msg


def state_vg(module, vg, state, vg_validation):
    vg_state, msg = vg_validation

    if vg_state is None:
        module.fail_json(msg=msg)

    if state == 'varyon':
        if vg_state is True:
            changed = False
            return changed, msg

        changed = True
        msg = ''
        if not module.check_mode:
            varyonvg_cmd = module.get_bin_path('varyonvg', True)
            rc, varyonvg_out, err = module.run_command([varyonvg_cmd, vg])
            if rc != 0:
                module.fail_json(msg="Command 'varyonvg' failed.", rc=rc, err=err)

        msg = "Varyon volume group %s completed." % vg
        return changed, msg

    elif state == 'varyoff':
        if vg_state is False:
            changed = False
            return changed, msg

        changed = True
        msg = ''

        if not module.check_mode:
            varyonvg_cmd = module.get_bin_path('varyoffvg', True)
            rc, varyonvg_out, stderr = module.run_command([varyonvg_cmd, vg])
            if rc != 0:
                module.fail_json(msg="Command 'varyoffvg' failed.", rc=rc, stdout=varyonvg_out, stderr=stderr)

        msg = "Varyoff volume group %s completed." % vg
        return changed, msg


def main():
    module = AnsibleModule(
        argument_spec=dict(
            force=dict(type='bool', default=False),
            pp_size=dict(type='int'),
            pvs=dict(type='list', elements='str'),
            state=dict(type='str', default='present', choices=['absent', 'present', 'varyoff', 'varyon']),
            vg=dict(type='str', required=True),
            vg_type=dict(type='str', default='normal', choices=['big', 'normal', 'scalable'])
        ),
        supports_check_mode=True,
    )

    force = module.params['force']
    pp_size = module.params['pp_size']
    pvs = module.params['pvs']
    state = module.params['state']
    vg = module.params['vg']
    vg_type = module.params['vg_type']

    if pp_size is None:
        pp_size = ''
    else:
        pp_size = "-s %s" % pp_size

    vg_validation = _validate_vg(module, vg)

    if state == 'present':
        if not pvs:
            changed = False
            msg = "pvs is required to state 'present'."
            module.fail_json(msg=msg)
        else:
            changed, msg = create_extend_vg(module, vg, pvs, pp_size, vg_type, force, vg_validation)

    elif state == 'absent':
        changed, msg = reduce_vg(module, vg, pvs, vg_validation)

    elif state == 'varyon' or state == 'varyoff':
        changed, msg = state_vg(module, vg, state, vg_validation)

    else:
        changed = False
        msg = "Unexpected state"

    module.exit_json(changed=changed, msg=msg, state=state)


if __name__ == '__main__':
    main()