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:
| Module | Plugin |
|---|---|
| Task ichida ishlatiladi | Ansible core bilan integratsiya |
| Specific operation bajaradi | Functionality extend qiladi |
| Playbook da ko'rinadi | Background da ishlaydi |
| Idempotent bo'lishi kerak | Process 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.