Skip to main content

Modules va Plugins

1. Ansible Modules - Asosiy Building Blocks

Module nima?

Module - bu Ansible ning asosiy ish birlik bo'lib, aniq bir vazifani bajaradigan kod qismi. Har bir task da bitta module ishlatiladi va u o'z parametrlari bilan ma'lum bir amalni amalga oshiradi.

Module ning asosiy xususiyatlari:

  • Idempotent - bir xil natija qayta-qayta ishlatganda
  • Declarative - nima qilish kerakligini belgilaydi, qanday qilishni emas
  • Return data - structured ma'lumot qaytaradi
  • Cross-platform - turli OS larda ishlaydi
  • Error handling - xatolarni to'g'ri handle qiladi

Module anatomiyasi:

Module ning tuzilishi:

#!/usr/bin/python
# Basic module structure

from ansible.module_utils.basic import AnsibleModule

def main():
# Module arguments definition
module_args = dict(
name=dict(type='str', required=True),
state=dict(type='str', default='present', choices=['present', 'absent']),
count=dict(type='int', default=1),
enabled=dict(type='bool', default=True)
)

# Create module instance
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)

# Get parameters
name = module.params['name']
state = module.params['state']

# Main logic here
if module.check_mode:
# Check mode - don't make changes
module.exit_json(changed=False, msg='Check mode')

# Perform actual operations
result = dict(
changed=False,
message='Operation completed'
)

# Return results
module.exit_json(**result)

if __name__ == '__main__':
main()

Built-in Module Categories:

1. System Modules:

  • user - foydalanuvchi boshqaruvi
  • group - guruh boshqaruvi
  • service - service lar boshqaruvi
  • systemd - systemd units boshqaruvi
  • cron - cron jobs
  • mount - file system mounting

2. File Modules:

  • copy - fayl nusxalash
  • file - fayl/directory operations
  • template - Jinja2 template processing
  • lineinfile - line-by-line file editing
  • blockinfile - block-based file editing
  • fetch - remote dan local ga fayl olish

3. Package Management:

  • package - generic package manager
  • apt - Debian/Ubuntu package manager
  • yum/dnf - RedHat/CentOS package manager
  • pip - Python packages
  • npm - Node.js packages

4. Cloud Modules:

  • ec2 - AWS EC2 instances
  • s3 - AWS S3 operations
  • azure_rm - Azure Resource Manager
  • gcp - Google Cloud Platform
  • docker_container - Docker container management

5. Database Modules:

  • mysql_db - MySQL database operations
  • postgresql_db - PostgreSQL operations
  • mongodb - MongoDB operations
  • redis - Redis operations

6. Network Modules:

  • uri - HTTP requests
  • get_url - file download
  • firewalld - firewall management
  • iptables - iptables rules

Module Usage Patterns:

Basic usage:

- name: Install nginx
apt:
name: nginx
state: present
update_cache: yes
become: yes

- name: Copy configuration file
copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: '0644'
backup: yes
notify: restart nginx

Advanced usage with return values:

- name: Check disk space
shell: df -h /
register: disk_usage
changed_when: false

- name: Show disk usage
debug:
msg: "Disk usage: {{ disk_usage.stdout }}"

- name: Get service status
service:
name: nginx
register: nginx_status
check_mode: yes

- name: Make decision based on service status
debug:
msg: "Nginx is {{ 'running' if nginx_status.status.ActiveState == 'active' else 'not running' }}"

Custom Module yaratish:

Simple custom module:

#!/usr/bin/python

DOCUMENTATION = '''
---
module: my_custom_module
short_description: Custom module example
description:
- This is a custom module example
- It demonstrates basic module development
options:
name:
description:
- Name parameter
required: true
type: str
count:
description:
- Count parameter
required: false
default: 1
type: int
state:
description:
- State parameter
required: false
default: present
choices: ['present', 'absent']
type: str
'''

EXAMPLES = '''
# Basic usage
- name: Use custom module
my_custom_module:
name: test_item
count: 5
state: present

# With variables
- name: Dynamic usage
my_custom_module:
name: "{{ item.name }}"
count: "{{ item.count }}"
loop: "{{ my_items }}"
'''

RETURN = '''
name:
description: The name that was processed
type: str
returned: always
sample: test_item
count:
description: The count that was used
type: int
returned: always
sample: 5
changed:
description: Whether the operation made changes
type: bool
returned: always
sample: true
message:
description: Operation result message
type: str
returned: always
sample: "Operation completed successfully"
'''

from ansible.module_utils.basic import AnsibleModule
import os

def main():
module_args = dict(
name=dict(type='str', required=True),
count=dict(type='int', default=1),
state=dict(type='str', default='present', choices=['present', 'absent'])
)

module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True
)

name = module.params['name']
count = module.params['count']
state = module.params['state']

# Example logic
file_path = f"/tmp/ansible_custom_{name}"

if state == 'present':
if not os.path.exists(file_path):
if not module.check_mode:
with open(file_path, 'w') as f:
f.write(f"Name: {name}\nCount: {count}\n")

result = dict(
changed=True,
name=name,
count=count,
message=f"Created file for {name}"
)
else:
result = dict(
changed=False,
name=name,
count=count,
message=f"File for {name} already exists"
)
else: # absent
if os.path.exists(file_path):
if not module.check_mode:
os.remove(file_path)

result = dict(
changed=True,
name=name,
count=count,
message=f"Removed file for {name}"
)
else:
result = dict(
changed=False,
name=name,
count=count,
message=f"File for {name} doesn't exist"
)

module.exit_json(**result)

if __name__ == '__main__':
main()

Module ni ishlatish:

# library/ papkasiga joylashtiring yoki
# Playbook bilan bir joyda library/ papkasi yarating

- name: Use custom module
my_custom_module:
name: webapp
count: 3
state: present
register: custom_result

- name: Show custom module result
debug:
var: custom_result

Module development best practices:

1. Documentation:

  • DOCUMENTATION string - module hujjatlash
  • EXAMPLES string - foydalanish misollari
  • RETURN string - qaytaradigan ma'lumotlar

2. Error handling:

try:
# Risky operation
result = perform_operation()
except Exception as e:
module.fail_json(msg=f"Operation failed: {str(e)}")

3. Check mode support:

if module.check_mode:
# Dry run - don't make actual changes
module.exit_json(changed=would_change, msg="Check mode")

4. Input validation:

module_args = dict(
path=dict(type='path', required=True),
mode=dict(type='str', default='0644'),
owner=dict(type='str'),
state=dict(type='str', choices=['present', 'absent'], default='present')
)

module = AnsibleModule(
argument_spec=module_args,
required_if=[
('state', 'present', ['owner'])
],
mutually_exclusive=[
('create', 'delete')
]
)

2. Ansible Plugins - Extending Functionality

Plugin nima?

Plugin - bu Ansible ning core functionality sini kengaytiradigan kod qismlari. Ular Ansible ning ishlash jarayonini turli bosqichlarda customize qilish imkonini beradi.

Plugin va Module farqi:

ModulePlugin
Task ichida ishlatiladiAnsible core bilan integratsiya
Specific operation bajaradiFunctionality extend qiladi
Playbook da ko'rinadiBackground da ishlaydi
Idempotent bo'lishi kerakProcess ni o'zgartiradi

Plugin Types:

1. Action Plugins:

  • Module execution dan oldin ishlaydi
  • Module behavior ni o'zgartirishi mumkin
  • Copy, template kabi modullar action plugin ishlatadi

2. Callback Plugins:

  • Playbook execution events ni handle qiladi
  • Logging, monitoring, notification uchun
  • Progress reporting, metrics collection

3. Connection Plugins:

  • Remote host larga qanday ulanishni belgilaydi
  • SSH, WinRM, Docker, Kubernetes connections
  • Custom connection methods

4. Lookup Plugins:

  • External data sources dan ma'lumot olish
  • Variables ga qiymat berish uchun
  • File, database, API lookups

5. Filter Plugins:

  • Jinja2 template da ma'lumot o'zgartirish
  • Custom data transformation
  • String, list, dict operations

6. Test Plugins:

  • Jinja2 conditional tests
  • Custom boolean checks
  • Data validation

7. Inventory Plugins:

  • Dynamic inventory generation
  • Cloud providers, databases
  • Custom inventory sources

8. Cache Plugins:

  • Fact caching mechanisms
  • Performance optimization
  • Custom storage backends

Lookup Plugins:

Built-in lookup plugins:

# File content lookup
- name: Read file content
debug:
msg: "{{ lookup('file', '/etc/hostname') }}"

# Environment variables
- name: Get environment variable
debug:
msg: "{{ lookup('env', 'HOME') }}"

# Password generation
- name: Generate random password
debug:
msg: "{{ lookup('password', '/tmp/passwords/user1 length=15 chars=ascii_letters,digits') }}"

# DNS lookup
- name: Get IP address
debug:
msg: "{{ lookup('dig', 'example.com') }}"

# CSV file lookup
- name: Read CSV data
debug:
msg: "{{ lookup('csvfile', 'user1 file=/etc/users.csv delimiter=,') }}"

Custom lookup plugin:

# lookup_plugins/custom_lookup.py

from ansible.plugins.lookup import LookupBase
from ansible.errors import AnsibleError
import requests

class LookupModule(LookupBase):
def run(self, terms, variables=None, **kwargs):
results = []

for term in terms:
try:
# Custom logic - API call example
response = requests.get(f"https://api.example.com/data/{term}")
if response.status_code == 200:
results.append(response.json())
else:
raise AnsibleError(f"API request failed: {response.status_code}")
except Exception as e:
raise AnsibleError(f"Lookup failed: {str(e)}")

return results

Ishlatish:

- name: Use custom lookup
debug:
msg: "{{ lookup('custom_lookup', 'user123') }}"

- name: Loop with custom lookup
debug:
msg: "User data: {{ item }}"
loop: "{{ lookup('custom_lookup', 'user1', 'user2', 'user3').split(',') }}"

Filter Plugins:

Built-in filter plugins:

# String filters
- name: String operations
debug:
msg: |
Original: {{ my_string }}
Upper: {{ my_string | upper }}
Replace: {{ my_string | replace('old', 'new') }}
Split: {{ my_string | split(',') }}

# List filters
- name: List operations
debug:
msg: |
Join: {{ my_list | join(' - ') }}
Unique: {{ my_list | unique }}
Sort: {{ my_list | sort }}
First: {{ my_list | first }}
Last: {{ my_list | last }}

# Dict filters
- name: Dictionary operations
debug:
msg: |
Keys: {{ my_dict | list }}
Values: {{ my_dict | dict2items }}
Combine: {{ dict1 | combine(dict2) }}

Custom filter plugin:

# filter_plugins/custom_filters.py

def reverse_string(string):
"""Reverse a string"""
return string[::-1]

def calculate_percentage(part, total):
"""Calculate percentage"""
if total == 0:
return 0
return round((part / total) * 100, 2)

def extract_domain(email):
"""Extract domain from email"""
if '@' in email:
return email.split('@')[1]
return ''

class FilterModule(object):
"""Custom filters"""

def filters(self):
return {
'reverse_string': reverse_string,
'percentage': calculate_percentage,
'extract_domain': extract_domain
}

Ishlatish:

- name: Use custom filters
debug:
msg: |
Reversed: {{ "hello world" | reverse_string }}
Percentage: {{ 25 | percentage(100) }}%
Domain: {{ "user@example.com" | extract_domain }}

Callback Plugins:

Custom callback plugin:

# callback_plugins/custom_callback.py

from ansible.plugins.callback import CallbackBase
import json

class CallbackModule(CallbackBase):
"""Custom callback for logging"""

CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'notification'
CALLBACK_NAME = 'custom_callback'

def __init__(self):
super(CallbackModule, self).__init__()
self.task_ok = 0
self.task_failed = 0

def v2_runner_on_ok(self, result):
"""Task succeeded"""
self.task_ok += 1
host = result._host.name
task_name = result._task.name

self._display.display(f"✅ SUCCESS: {host} - {task_name}")

def v2_runner_on_failed(self, result, ignore_errors=False):
"""Task failed"""
self.task_failed += 1
host = result._host.name
task_name = result._task.name

self._display.display(f"❌ FAILED: {host} - {task_name}")

def v2_playbook_on_stats(self, stats):
"""Playbook finished"""
self._display.display(f"\n📊 SUMMARY: {self.task_ok} OK, {self.task_failed} FAILED")

Callback plugin ni yoqish:

# ansible.cfg
[defaults]
callback_plugins = ./callback_plugins
stdout_callback = custom_callback

Connection Plugins:

Custom connection plugin example:

# connection_plugins/custom_connection.py

from ansible.plugins.connection import ConnectionBase
from ansible.errors import AnsibleConnectionFailure
import paramiko

class Connection(ConnectionBase):
"""Custom SSH connection with special handling"""

transport = 'custom_ssh'

def __init__(self, play_context, new_stdin, *args, **kwargs):
super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs)
self.ssh_client = None

def _connect(self):
"""Establish connection"""
if self.ssh_client:
return self

self.ssh_client = paramiko.SSHClient()
self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

try:
self.ssh_client.connect(
hostname=self._play_context.remote_addr,
username=self._play_context.remote_user,
password=self._play_context.password,
key_filename=self._play_context.private_key_file,
timeout=self._play_context.timeout
)
except Exception as e:
raise AnsibleConnectionFailure(f"Failed to connect: {str(e)}")

return self

def exec_command(self, cmd, in_data=None, sudoable=True):
"""Execute command"""
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)

self._connect()

stdin, stdout, stderr = self.ssh_client.exec_command(cmd)

return (
0, # return code
stdout.read().decode('utf-8'), # stdout
stderr.read().decode('utf-8') # stderr
)

def put_file(self, in_path, out_path):
"""Upload file"""
self._connect()
sftp = self.ssh_client.open_sftp()
sftp.put(in_path, out_path)
sftp.close()

def fetch_file(self, in_path, out_path):
"""Download file"""
self._connect()
sftp = self.ssh_client.open_sftp()
sftp.get(in_path, out_path)
sftp.close()

def close(self):
"""Close connection"""
if self.ssh_client:
self.ssh_client.close()
self.ssh_client = None

Inventory Plugins:

Custom inventory plugin:

# inventory_plugins/custom_inventory.py

from ansible.plugins.inventory import BaseInventoryPlugin
import json
import requests

class InventoryModule(BaseInventoryPlugin):
"""Custom inventory from API"""

NAME = 'custom_inventory'

def verify_file(self, path):
"""Verify inventory file"""
return path.endswith(('custom_inventory.yml', 'custom_inventory.yaml'))

def parse(self, inventory, loader, path, cache=False):
"""Parse inventory"""
super(InventoryModule, self).parse(inventory, loader, path, cache)

# Get configuration
config = self._read_config_data(path)
api_url = config.get('api_url', 'https://api.example.com/inventory')

try:
# Fetch data from API
response = requests.get(api_url)
data = response.json()

# Process hosts
for host_data in data.get('hosts', []):
host_name = host_data['name']

# Add host
self.inventory.add_host(host_name)

# Set host variables
for key, value in host_data.get('vars', {}).items():
self.inventory.set_variable(host_name, key, value)

# Add to groups
for group_name in host_data.get('groups', []):
self.inventory.add_group(group_name)
self.inventory.add_child(group_name, host_name)

except Exception as e:
raise AnsibleError(f"Failed to parse inventory: {str(e)}")

Inventory plugin configuration:

# custom_inventory.yml
plugin: custom_inventory
api_url: https://api.mycompany.com/servers
timeout: 30
cache: true
cache_timeout: 300

Plugin Development Best Practices:

1. Plugin Structure:

my_collection/
├── plugins/
│ ├── action/
│ ├── callback/
│ ├── connection/
│ ├── filter/
│ ├── inventory/
│ ├── lookup/
│ └── test/
└── meta/
└── runtime.yml

2. Error Handling:

from ansible.errors import AnsibleError, AnsibleFilterError

try:
# Plugin logic
result = process_data(input_data)
return result
except ValueError as e:
raise AnsibleFilterError(f"Invalid input: {str(e)}")
except Exception as e:
raise AnsibleError(f"Plugin failed: {str(e)}")

3. Configuration:

# Plugin configuration support
def get_option(self, option):
"""Get plugin option"""
return self.get_option(option)

def set_options(self, task_keys=None, var_options=None, direct=None):
"""Set plugin options"""
super().set_options(task_keys, var_options, direct)

4. Testing:

# Unit tests for plugins
import unittest
from ansible.plugins.filter.custom_filters import FilterModule

class TestCustomFilters(unittest.TestCase):
def setUp(self):
self.filter_module = FilterModule()
self.filters = self.filter_module.filters()

def test_reverse_string(self):
result = self.filters['reverse_string']('hello')
self.assertEqual(result, 'olleh')

def test_percentage(self):
result = self.filters['percentage'](25, 100)
self.assertEqual(result, 25.0)

Bu comprehensive guide Ansible Modules va Plugins ni professional darajada ishlatish va yaratish uchun barcha kerakli bilimlarni beradi.