28.2 Ανατομία Ansible Module 🔬

Minimal module structure

#!/usr/bin/python
# library/my_service_status.py

DOCUMENTATION = r'''
---
module: my_service_status
short_description: Manage custom service via API
description:
  - Creates, updates or removes a service entry via REST API
options:
  name:
    description: Service name
    required: true
    type: str
  state:
    description: Desired state
    choices: [present, absent]
    default: present
    type: str
  api_url:
    description: API base URL
    required: true
    type: str
  api_token:
    description: API authentication token
    required: true
    no_log: true
    type: str
'''

EXAMPLES = r'''
- name: Register service
  my_service_status:
    name:      myapp
    state:     present
    api_url:   "https://cmdb.example.com/api"
    api_token: "{{ vault_cmdb_token }}"
'''

RETURN = r'''
service:
  description: Service object from API
  returned: always
  type: dict
changed:
  description: Whether the state was changed
  returned: always
  type: bool
'''

# ── Imports ───────────────────────────────────────────
import json
import traceback
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import open_url

# ── Main function ─────────────────────────────────────
def main():
    # 1. Ορισμός arguments
    module_args = dict(
        name      = dict(type='str', required=True),
        state     = dict(type='str', default='present',
                         choices=['present', 'absent']),
        api_url   = dict(type='str', required=True),
        api_token = dict(type='str', required=True, no_log=True),
    )

    # 2. Δημιουργία module object
    module = AnsibleModule(
        argument_spec       = module_args,
        supports_check_mode = True,  # ← --check support
    )

    # 3. Ανάγνωση parameters
    name      = module.params['name']
    state     = module.params['state']
    api_url   = module.params['api_url']
    api_token = module.params['api_token']
    headers   = {'Authorization': f'Bearer {api_token}',
                 'Content-Type': 'application/json'}

    # 4. Τρέχουσα κατάσταση (για idempotency)
    try:
        response = open_url(
            f'{api_url}/services/{name}',
            headers=headers,
            method='GET'
        )
        exists = True
        current = json.loads(response.read())
    except Exception:
        exists = False
        current = {}

    # 5. Idempotency checks
    if state == 'present' and exists:
        module.exit_json(changed=False, service=current)

    if state == 'absent' and not exists:
        module.exit_json(changed=False, service={})

    # 6. Check mode — δεν κάνει τίποτα
    if module.check_mode:
        module.exit_json(changed=True)

    # 7. Εκτέλεση αλλαγής
    try:
        if state == 'present':
            response = open_url(
                f'{api_url}/services',
                headers=headers,
                method='POST',
                data=json.dumps({'name': name}).encode()
            )
            result = json.loads(response.read())
            module.exit_json(changed=True, service=result)

        else:  # state == 'absent'
            open_url(
                f'{api_url}/services/{name}',
                headers=headers,
                method='DELETE'
            )
            module.exit_json(changed=True, service={})

    except Exception as e:
        module.fail_json(
            msg=f'API error: {str(e)}',
            exception=traceback.format_exc()
        )


if __name__ == '__main__':
    main()

Χρήση custom module

# playbook.yml (στο ίδιο project)
tasks:

  - name: Register service
    my_service_status:          # ← χρήση!
      name:      myapp
      state:     present
      api_url:   "https://cmdb.example.com/api"
      api_token: "{{ vault_cmdb_token }}"
    register: service_result

  - name: Service info
    ansible.builtin.debug:
      var: service_result.service

Σύνοψη 28.2

Module anatomy:
│
├── DOCUMENTATION, EXAMPLES, RETURN (strings)
├── AnsibleModule(argument_spec, supports_check_mode)
├── module.params['param_name']
├── Idempotency: έλεγχος πριν αλλαγή
├── module.check_mode: True → no changes
├── module.exit_json(changed=bool, ...)
└── module.fail_json(msg='error')