0
# Module Utilities
1
2
Ansible Core's module utilities provide comprehensive shared libraries for module development including argument parsing, JSON handling, text conversion, network utilities, validation, platform abstraction, and common functionality used across all Ansible modules.
3
4
## Capabilities
5
6
### Basic Module Framework
7
8
Core module utilities providing the foundation for all Ansible module development with argument parsing, result formatting, and platform abstraction.
9
10
```python { .api }
11
class AnsibleModule:
12
"""
13
Base class for all Ansible modules providing common functionality.
14
15
Parameters:
16
- argument_spec: Module argument specification
17
- bypass_checks: Skip argument validation
18
- no_log: Disable logging of arguments
19
- check_invalid_arguments: Validate unknown arguments
20
- mutually_exclusive: Mutually exclusive argument groups
21
- required_together: Arguments required together
22
- required_one_of: Require at least one of arguments
23
- add_file_common_args: Add common file arguments
24
- supports_check_mode: Module supports check mode
25
26
Attributes:
27
- params: Parsed module parameters
28
- check_mode: Whether in check mode
29
- diff_mode: Whether in diff mode
30
"""
31
32
def __init__(self, argument_spec, bypass_checks=False, no_log=False,
33
check_invalid_arguments=None, mutually_exclusive=None,
34
required_together=None, required_one_of=None,
35
add_file_common_args=False, supports_check_mode=False):
36
"""Initialize module with argument specification"""
37
38
def exit_json(self, **kwargs):
39
"""
40
Exit module with success result.
41
42
Parameters:
43
- kwargs: Result data (changed, msg, etc.)
44
"""
45
46
def fail_json(self, msg, **kwargs):
47
"""
48
Exit module with failure result.
49
50
Parameters:
51
- msg: Failure message
52
- kwargs: Additional result data
53
"""
54
55
def run_command(self, args, check_rc=True, close_fds=True, executable=None,
56
data=None, binary_data=False, path_prefix=None, cwd=None,
57
use_unsafe_shell=False, prompt_regex=None, environ_update=None,
58
umask=None, encoding='utf-8', errors='surrogateescape'):
59
"""
60
Execute command and return result.
61
62
Parameters:
63
- args: Command arguments
64
- check_rc: Check return code
65
- close_fds: Close file descriptors
66
- executable: Shell executable
67
- data: Input data
68
- binary_data: Handle binary data
69
- path_prefix: PATH prefix
70
- cwd: Working directory
71
- use_unsafe_shell: Allow shell injection
72
- prompt_regex: Regex for prompts
73
- environ_update: Environment updates
74
- umask: Process umask
75
- encoding: Text encoding
76
- errors: Error handling
77
78
Returns:
79
tuple: (return_code, stdout, stderr)
80
"""
81
82
def get_bin_path(self, arg, required=False, opt_dirs=None):
83
"""
84
Find binary in PATH.
85
86
Parameters:
87
- arg: Binary name
88
- required: Whether binary is required
89
- opt_dirs: Additional directories to search
90
91
Returns:
92
str: Path to binary or None
93
"""
94
95
def boolean(self, arg):
96
"""
97
Convert argument to boolean.
98
99
Parameters:
100
- arg: Value to convert
101
102
Returns:
103
bool: Boolean value
104
"""
105
106
def md5(self, filename):
107
"""
108
Calculate MD5 hash of file.
109
110
Parameters:
111
- filename: File path
112
113
Returns:
114
str: MD5 hash
115
"""
116
117
def sha1(self, filename):
118
"""
119
Calculate SHA1 hash of file.
120
121
Parameters:
122
- filename: File path
123
124
Returns:
125
str: SHA1 hash
126
"""
127
128
def backup_file(self, fn):
129
"""
130
Create backup of file.
131
132
Parameters:
133
- fn: File path
134
135
Returns:
136
str: Backup file path
137
"""
138
139
def cleanup(self, tmpfile):
140
"""
141
Clean up temporary files.
142
143
Parameters:
144
- tmpfile: Temporary file path
145
"""
146
```
147
148
### Argument Specification and Validation
149
150
Comprehensive argument parsing and validation system supporting complex argument relationships, type checking, and input sanitization.
151
152
```python { .api }
153
def check_required_arguments(argument_spec, parameters):
154
"""
155
Check that all required arguments are provided.
156
157
Parameters:
158
- argument_spec: Argument specification
159
- parameters: Provided parameters
160
161
Raises:
162
AnsibleError: If required arguments missing
163
"""
164
165
def check_type_str(value):
166
"""
167
Ensure value is string type.
168
169
Parameters:
170
- value: Value to check
171
172
Returns:
173
str: String value
174
"""
175
176
def check_type_list(value):
177
"""
178
Ensure value is list type.
179
180
Parameters:
181
- value: Value to check
182
183
Returns:
184
list: List value
185
"""
186
187
def check_type_dict(value):
188
"""
189
Ensure value is dictionary type.
190
191
Parameters:
192
- value: Value to check
193
194
Returns:
195
dict: Dictionary value
196
"""
197
198
def check_type_bool(value):
199
"""
200
Convert value to boolean.
201
202
Parameters:
203
- value: Value to convert
204
205
Returns:
206
bool: Boolean value
207
"""
208
209
def check_type_int(value):
210
"""
211
Convert value to integer.
212
213
Parameters:
214
- value: Value to convert
215
216
Returns:
217
int: Integer value
218
"""
219
220
def check_type_float(value):
221
"""
222
Convert value to float.
223
224
Parameters:
225
- value: Value to convert
226
227
Returns:
228
float: Float value
229
"""
230
231
def check_type_path(value):
232
"""
233
Validate and expand file path.
234
235
Parameters:
236
- value: Path to validate
237
238
Returns:
239
str: Validated path
240
"""
241
```
242
243
### Text Processing and Conversion
244
245
Text handling utilities for encoding conversion, string manipulation, and cross-platform text processing.
246
247
```python { .api }
248
def to_bytes(obj, encoding='utf-8', errors='surrogateescape', nonstring='simplerepr'):
249
"""
250
Convert object to bytes.
251
252
Parameters:
253
- obj: Object to convert
254
- encoding: Text encoding
255
- errors: Error handling
256
- nonstring: Non-string handling
257
258
Returns:
259
bytes: Byte representation
260
"""
261
262
def to_text(obj, encoding='utf-8', errors='surrogateescape', nonstring='simplerepr'):
263
"""
264
Convert object to text string.
265
266
Parameters:
267
- obj: Object to convert
268
- encoding: Text encoding
269
- errors: Error handling
270
- nonstring: Non-string handling
271
272
Returns:
273
str: Text representation
274
"""
275
276
def to_native(obj, encoding='utf-8', errors='surrogateescape', nonstring='simplerepr'):
277
"""
278
Convert object to native string type.
279
280
Parameters:
281
- obj: Object to convert
282
- encoding: Text encoding
283
- errors: Error handling
284
- nonstring: Non-string handling
285
286
Returns:
287
str: Native string
288
"""
289
```
290
291
### JSON Handling
292
293
JSON processing utilities with enhanced error handling and Ansible-specific serialization support.
294
295
```python { .api }
296
def load_json(data):
297
"""
298
Load JSON data with error handling.
299
300
Parameters:
301
- data: JSON string
302
303
Returns:
304
object: Parsed JSON data
305
306
Raises:
307
ValueError: If JSON parsing fails
308
"""
309
310
def dump_json(obj, indent=None):
311
"""
312
Dump object to JSON string.
313
314
Parameters:
315
- obj: Object to serialize
316
- indent: JSON indentation
317
318
Returns:
319
str: JSON string
320
"""
321
322
class AnsibleJSONEncoder:
323
"""JSON encoder with Ansible-specific type handling"""
324
325
def default(self, obj):
326
"""Handle Ansible-specific object types"""
327
```
328
329
### Network Utilities
330
331
Network-related utilities for IP address validation, network calculations, and network interface operations.
332
333
```python { .api }
334
def is_ip(address):
335
"""
336
Check if string is valid IP address.
337
338
Parameters:
339
- address: Address to check
340
341
Returns:
342
bool: True if valid IP
343
"""
344
345
def is_ipv4(address):
346
"""Check if address is IPv4"""
347
348
def is_ipv6(address):
349
"""Check if address is IPv6"""
350
351
def get_network_address(ip, prefix):
352
"""
353
Calculate network address from IP and prefix.
354
355
Parameters:
356
- ip: IP address
357
- prefix: Network prefix
358
359
Returns:
360
str: Network address
361
"""
362
363
def get_network_size(prefix):
364
"""
365
Get network size from prefix.
366
367
Parameters:
368
- prefix: Network prefix
369
370
Returns:
371
int: Number of addresses
372
"""
373
```
374
375
### File and Directory Operations
376
377
File system utilities for cross-platform file operations, permission handling, and path manipulation.
378
379
```python { .api }
380
def mkstemp(suffix="", prefix="tmp", dir=None):
381
"""
382
Create temporary file.
383
384
Parameters:
385
- suffix: File suffix
386
- prefix: File prefix
387
- dir: Directory for temp file
388
389
Returns:
390
tuple: (file_descriptor, path)
391
"""
392
393
def mkdtemp(suffix="", prefix="tmp", dir=None):
394
"""
395
Create temporary directory.
396
397
Parameters:
398
- suffix: Directory suffix
399
- prefix: Directory prefix
400
- dir: Parent directory
401
402
Returns:
403
str: Temporary directory path
404
"""
405
406
def cleanup_tmp_file(path):
407
"""
408
Clean up temporary file.
409
410
Parameters:
411
- path: File path to clean up
412
"""
413
414
def set_mode_if_different(path, mode, changed):
415
"""
416
Set file mode if different.
417
418
Parameters:
419
- path: File path
420
- mode: Desired mode
421
- changed: Whether file was changed
422
423
Returns:
424
bool: True if mode was changed
425
"""
426
427
def set_owner_if_different(path, owner, changed):
428
"""
429
Set file owner if different.
430
431
Parameters:
432
- path: File path
433
- owner: Desired owner
434
- changed: Whether file was changed
435
436
Returns:
437
bool: True if owner was changed
438
"""
439
440
def set_group_if_different(path, group, changed):
441
"""
442
Set file group if different.
443
444
Parameters:
445
- path: File path
446
- group: Desired group
447
- changed: Whether file was changed
448
449
Returns:
450
bool: True if group was changed
451
"""
452
```
453
454
### Dictionary Transformations
455
456
Dictionary manipulation utilities for merging, flattening, and transforming data structures commonly used in automation.
457
458
```python { .api }
459
def dict_merge(base_dict, other_dict):
460
"""
461
Recursively merge dictionaries.
462
463
Parameters:
464
- base_dict: Base dictionary
465
- other_dict: Dictionary to merge
466
467
Returns:
468
dict: Merged dictionary
469
"""
470
471
def flatten_dict(d, separator='.'):
472
"""
473
Flatten nested dictionary.
474
475
Parameters:
476
- d: Dictionary to flatten
477
- separator: Key separator
478
479
Returns:
480
dict: Flattened dictionary
481
"""
482
483
def camel_dict_to_snake_dict(camel_dict):
484
"""
485
Convert camelCase keys to snake_case.
486
487
Parameters:
488
- camel_dict: Dictionary with camelCase keys
489
490
Returns:
491
dict: Dictionary with snake_case keys
492
"""
493
494
def snake_dict_to_camel_dict(snake_dict, capitalize_first=False):
495
"""
496
Convert snake_case keys to camelCase.
497
498
Parameters:
499
- snake_dict: Dictionary with snake_case keys
500
- capitalize_first: Capitalize first letter
501
502
Returns:
503
dict: Dictionary with camelCase keys
504
"""
505
```
506
507
### Process and System Utilities
508
509
System-level utilities for process management, signal handling, and platform-specific operations.
510
511
```python { .api }
512
def get_platform():
513
"""
514
Get current platform name.
515
516
Returns:
517
str: Platform name (Linux, Darwin, Windows, etc.)
518
"""
519
520
def get_distribution():
521
"""
522
Get Linux distribution information.
523
524
Returns:
525
tuple: (name, version, codename)
526
"""
527
528
def get_all_subclasses(cls):
529
"""
530
Get all subclasses of a class.
531
532
Parameters:
533
- cls: Base class
534
535
Returns:
536
set: All subclasses
537
"""
538
539
def run_cmd(module, args, **kwargs):
540
"""
541
Run command using module's run_command method.
542
543
Parameters:
544
- module: AnsibleModule instance
545
- args: Command arguments
546
- kwargs: Additional options
547
548
Returns:
549
tuple: (return_code, stdout, stderr)
550
"""
551
```
552
553
### Validation Utilities
554
555
Input validation and sanitization utilities for securing module inputs and ensuring data integrity.
556
557
```python { .api }
558
def check_required_if(parameters, argument_spec):
559
"""
560
Check conditional requirements.
561
562
Parameters:
563
- parameters: Module parameters
564
- argument_spec: Argument specification
565
566
Raises:
567
AnsibleError: If requirements not met
568
"""
569
570
def check_required_one_of(parameters, required_one_of):
571
"""
572
Check that one of required arguments is provided.
573
574
Parameters:
575
- parameters: Module parameters
576
- required_one_of: Required argument groups
577
578
Raises:
579
AnsibleError: If no required arguments provided
580
"""
581
582
def check_required_together(parameters, required_together):
583
"""
584
Check that related arguments are provided together.
585
586
Parameters:
587
- parameters: Module parameters
588
- required_together: Related argument groups
589
590
Raises:
591
AnsibleError: If related arguments not together
592
"""
593
594
def check_mutually_exclusive(parameters, mutually_exclusive):
595
"""
596
Check that mutually exclusive arguments are not both provided.
597
598
Parameters:
599
- parameters: Module parameters
600
- mutually_exclusive: Mutually exclusive groups
601
602
Raises:
603
AnsibleError: If exclusive arguments both provided
604
"""
605
606
def sanitize_keys(obj, no_log_strings):
607
"""
608
Remove sensitive data from object.
609
610
Parameters:
611
- obj: Object to sanitize
612
- no_log_strings: Sensitive strings to remove
613
614
Returns:
615
object: Sanitized object
616
"""
617
```
618
619
## Module Development Patterns
620
621
### Basic Module Structure
622
623
```python
624
#!/usr/bin/python
625
# -*- coding: utf-8 -*-
626
627
from ansible.module_utils.basic import AnsibleModule
628
629
DOCUMENTATION = '''
630
---
631
module: my_module
632
short_description: Example module
633
description:
634
- This is an example module
635
options:
636
name:
637
description:
638
- Name parameter
639
required: true
640
type: str
641
state:
642
description:
643
- Desired state
644
choices: ['present', 'absent']
645
default: present
646
type: str
647
'''
648
649
EXAMPLES = '''
650
- name: Ensure resource is present
651
my_module:
652
name: example
653
state: present
654
'''
655
656
RETURN = '''
657
changed:
658
description: Whether changes were made
659
type: bool
660
returned: always
661
message:
662
description: Result message
663
type: str
664
returned: always
665
'''
666
667
def main():
668
argument_spec = dict(
669
name=dict(type='str', required=True),
670
state=dict(type='str', default='present', choices=['present', 'absent'])
671
)
672
673
module = AnsibleModule(
674
argument_spec=argument_spec,
675
supports_check_mode=True
676
)
677
678
name = module.params['name']
679
state = module.params['state']
680
changed = False
681
682
if module.check_mode:
683
module.exit_json(changed=changed, msg="Check mode - no changes made")
684
685
# Module logic here
686
if state == 'present':
687
# Create/update resource
688
changed = True
689
msg = f"Resource {name} created"
690
else:
691
# Remove resource
692
changed = True
693
msg = f"Resource {name} removed"
694
695
module.exit_json(changed=changed, msg=msg)
696
697
if __name__ == '__main__':
698
main()
699
```
700
701
### Advanced Module with Validation
702
703
```python
704
from ansible.module_utils.basic import AnsibleModule
705
from ansible.module_utils.common.validation import check_required_arguments
706
707
def validate_parameters(module):
708
"""Custom parameter validation"""
709
params = module.params
710
711
if params['state'] == 'present' and not params.get('config'):
712
module.fail_json(msg="config required when state is present")
713
714
if params.get('port') and not (1 <= params['port'] <= 65535):
715
module.fail_json(msg="port must be between 1 and 65535")
716
717
def main():
718
argument_spec = dict(
719
name=dict(type='str', required=True),
720
state=dict(type='str', default='present', choices=['present', 'absent']),
721
config=dict(type='dict'),
722
port=dict(type='int'),
723
enable_ssl=dict(type='bool', default=False),
724
ssl_cert=dict(type='path'),
725
ssl_key=dict(type='path', no_log=True)
726
)
727
728
module = AnsibleModule(
729
argument_spec=argument_spec,
730
supports_check_mode=True,
731
required_together=[('ssl_cert', 'ssl_key')],
732
required_if=[('enable_ssl', True, ('ssl_cert', 'ssl_key'))]
733
)
734
735
# Custom validation
736
validate_parameters(module)
737
738
# Module implementation
739
result = {'changed': False}
740
741
try:
742
# Execute module logic
743
if module.params['state'] == 'present':
744
# Implementation here
745
result['changed'] = True
746
result['msg'] = 'Resource created successfully'
747
748
module.exit_json(**result)
749
750
except Exception as e:
751
module.fail_json(msg=f"Module failed: {str(e)}")
752
753
if __name__ == '__main__':
754
main()
755
```
756
757
## Usage Examples
758
759
### Using Module Utils in Custom Modules
760
761
```python
762
from ansible.module_utils.basic import AnsibleModule
763
from ansible.module_utils.common.text.converters import to_text, to_bytes
764
from ansible.module_utils.common.validation import check_type_dict
765
from ansible.module_utils.common.dict_transformations import dict_merge
766
import json
767
768
def process_config(module, config_data):
769
"""Process configuration with utilities"""
770
771
# Validate input
772
config = check_type_dict(config_data)
773
774
# Convert text encoding
775
processed_config = {}
776
for key, value in config.items():
777
key_text = to_text(key)
778
value_text = to_text(value) if isinstance(value, (str, bytes)) else value
779
processed_config[key_text] = value_text
780
781
# Merge with defaults
782
defaults = {'timeout': 30, 'retries': 3}
783
final_config = dict_merge(defaults, processed_config)
784
785
return final_config
786
787
def run_external_command(module, cmd):
788
"""Execute external command safely"""
789
790
rc, stdout, stderr = module.run_command(cmd, check_rc=False)
791
792
if rc != 0:
793
module.fail_json(
794
msg=f"Command failed: {cmd}",
795
rc=rc,
796
stdout=stdout,
797
stderr=stderr
798
)
799
800
return to_text(stdout).strip()
801
```
802
803
### Error Handling and Logging
804
805
```python
806
def safe_operation(module):
807
"""Example of safe operation with error handling"""
808
809
try:
810
# Risky operation
811
result = perform_operation()
812
813
# Sanitize sensitive data
814
from ansible.module_utils.common.parameters import sanitize_keys
815
safe_result = sanitize_keys(result, ['password', 'secret'])
816
817
return safe_result
818
819
except Exception as e:
820
module.fail_json(
821
msg=f"Operation failed: {str(e)}",
822
exception=traceback.format_exc()
823
)
824
```