0
# Utilities and Cleanup
1
2
Helper functions for artifact management, cleanup operations, output handling, and system integration. These utilities provide essential support functions for managing ansible-runner operations and maintaining system resources.
3
4
## Capabilities
5
6
### Cleanup Functions
7
8
Functions for managing temporary files, artifacts, and system resources.
9
10
```python { .api }
11
def cleanup_folder(folder: str) -> bool:
12
"""
13
Delete folder and return whether a change happened.
14
15
Args:
16
folder (str): Path to folder to delete
17
18
Returns:
19
bool: True if folder was deleted, False if folder didn't exist
20
"""
21
22
def register_for_cleanup(folder: str) -> None:
23
"""
24
Register a folder path for cleanup when execution finishes.
25
The folder need not exist at the time when this is called.
26
27
Args:
28
folder (str): Path to folder to register for cleanup
29
"""
30
```
31
32
Usage examples:
33
34
```python
35
from ansible_runner.utils import cleanup_folder, register_for_cleanup
36
37
# Clean up specific folder
38
success = cleanup_folder('/tmp/ansible-artifacts-12345')
39
if success:
40
print("Folder cleaned up successfully")
41
42
# Register folder for automatic cleanup on exit
43
register_for_cleanup('/tmp/ansible-temp-67890')
44
```
45
46
### Bunch Class
47
48
A utility class for collecting variables together in a dynamic object.
49
50
```python { .api }
51
class Bunch:
52
"""
53
Collect a bunch of variables together in an object.
54
This is a slight modification of Alex Martelli's and Doug Hudgeon's Bunch pattern.
55
"""
56
57
def __init__(self, **kwargs):
58
"""Initialize with keyword arguments as attributes"""
59
60
def update(self, **kwargs):
61
"""Update the object with new keyword arguments"""
62
63
def get(self, key):
64
"""Get attribute value by key name"""
65
```
66
67
Usage examples:
68
69
```python
70
from ansible_runner.utils import Bunch
71
72
# Create configuration object
73
config = Bunch(
74
host='localhost',
75
port=22,
76
user='ansible',
77
timeout=30
78
)
79
80
# Access attributes
81
print(f"Connecting to {config.host}:{config.port}")
82
83
# Update configuration
84
config.update(timeout=60, retries=3)
85
86
# Get attribute with default
87
timeout = config.get('timeout') or 30
88
```
89
90
### Helper Functions
91
92
Various utility functions for working with Ansible data and artifacts.
93
94
```python { .api }
95
def get_plugin_dir() -> str:
96
"""Get the path to the ansible-runner plugin directory"""
97
98
def get_callback_dir() -> str:
99
"""Get the path to the callback plugin directory"""
100
101
def is_dir_owner(directory: str) -> bool:
102
"""
103
Check if current user is the owner of directory.
104
105
Args:
106
directory (str): Path to directory to check
107
108
Returns:
109
bool: True if current user owns the directory
110
"""
111
112
def isplaybook(obj) -> bool:
113
"""
114
Inspect object and return if it is a playbook.
115
116
Args:
117
obj: The object to inspect
118
119
Returns:
120
bool: True if the object is a list (playbook format)
121
"""
122
123
def isinventory(obj) -> bool:
124
"""
125
Inspect object and return if it is an inventory.
126
127
Args:
128
obj: The object to inspect
129
130
Returns:
131
bool: True if the object is an inventory dict or string
132
"""
133
```
134
135
Usage examples:
136
137
```python
138
from ansible_runner.utils import (
139
get_plugin_dir, get_callback_dir, is_dir_owner,
140
isplaybook, isinventory
141
)
142
143
# Get plugin directories
144
plugin_dir = get_plugin_dir()
145
callback_dir = get_callback_dir()
146
147
# Check directory ownership
148
if is_dir_owner('/path/to/ansible/project'):
149
print("You own this directory")
150
151
# Validate data types
152
playbook_data = [{'hosts': 'all', 'tasks': []}]
153
inventory_data = {'all': {'hosts': ['server1', 'server2']}}
154
155
if isplaybook(playbook_data):
156
print("Valid playbook format")
157
158
if isinventory(inventory_data):
159
print("Valid inventory format")
160
```
161
162
### Artifact Management
163
164
Functions for handling execution artifacts and output data.
165
166
```python { .api }
167
def dump_artifact(obj, path: str, filename: str = None):
168
"""
169
Dump artifact object to file.
170
171
Args:
172
obj: Object to serialize and save
173
path (str): Directory path to save artifact
174
filename (str, optional): Filename for the artifact
175
"""
176
177
def ensure_str(obj):
178
"""
179
Ensure object is converted to string format.
180
181
Args:
182
obj: Object to convert to string
183
184
Returns:
185
str: String representation of object
186
"""
187
188
def collect_new_events(artifact_dir: str, old_events=None):
189
"""
190
Collect new events from artifact directory.
191
192
Args:
193
artifact_dir (str): Path to artifact directory
194
old_events: Previously collected events to filter out
195
196
Returns:
197
list: New events since last collection
198
"""
199
200
def cleanup_artifact_dir(artifact_dir: str, remove_dir: bool = False):
201
"""
202
Clean up artifact directory contents.
203
204
Args:
205
artifact_dir (str): Path to artifact directory
206
remove_dir (bool): Whether to remove the directory itself
207
"""
208
```
209
210
Usage examples:
211
212
```python
213
from ansible_runner.utils import (
214
dump_artifact, ensure_str, collect_new_events, cleanup_artifact_dir
215
)
216
217
# Save execution results
218
results = {'status': 'successful', 'rc': 0}
219
dump_artifact(results, '/tmp/artifacts', 'execution_results.json')
220
221
# Ensure string conversion
222
host_data = {'hostname': 'server1', 'ip': '192.168.1.10'}
223
host_str = ensure_str(host_data)
224
225
# Collect new events
226
artifact_dir = '/tmp/ansible-artifacts'
227
new_events = collect_new_events(artifact_dir)
228
print(f"Found {len(new_events)} new events")
229
230
# Clean up artifacts
231
cleanup_artifact_dir(artifact_dir, remove_dir=True)
232
```
233
234
### Output and Logging
235
236
Functions for handling output, logging, and display operations.
237
238
```python { .api }
239
def display(msg: str, log_only: bool = False) -> None:
240
"""
241
Display message to output.
242
243
Args:
244
msg (str): Message to display
245
log_only (bool): Only log the message, don't display
246
"""
247
248
def debug(msg: str) -> None:
249
"""
250
Debug logging function.
251
252
Args:
253
msg (str): Debug message to log
254
"""
255
256
def set_logfile(filename: str) -> None:
257
"""
258
Set log file for output.
259
260
Args:
261
filename (str): Path to log file
262
"""
263
264
def set_debug(value: str) -> None:
265
"""
266
Set debug level.
267
268
Args:
269
value (str): Debug level ('enable' or 'disable')
270
"""
271
272
def configure() -> None:
273
"""Configure logging system"""
274
```
275
276
Usage examples:
277
278
```python
279
from ansible_runner import output
280
281
# Configure logging
282
output.configure()
283
output.set_logfile('/var/log/ansible-runner.log')
284
output.set_debug('enable')
285
286
# Display messages
287
output.display("Starting playbook execution")
288
output.debug("Debug information for troubleshooting")
289
290
# Log-only messages
291
output.display("Internal status update", log_only=True)
292
```
293
294
### Cleanup Operations
295
296
Advanced cleanup operations for managing ansible-runner resources.
297
298
```python { .api }
299
def add_cleanup_args(command) -> None:
300
"""
301
Add cleanup arguments to argument parser.
302
303
Args:
304
command: ArgumentParser instance to add arguments to
305
"""
306
307
def run_cleanup(**kwargs):
308
"""
309
Run cleanup operations based on provided arguments.
310
311
Supported arguments:
312
- file_pattern (str): File glob pattern for directories to remove
313
- exclude_strings (list): Keywords to avoid when deleting
314
- remove_images (list): Container images to remove
315
- grace_period (int): Time in minutes before considering files for deletion
316
- process_isolation_executable (str): Container runtime to use
317
"""
318
```
319
320
Usage examples:
321
322
```python
323
import argparse
324
from ansible_runner.cleanup import add_cleanup_args, run_cleanup
325
326
# Setup argument parser with cleanup options
327
parser = argparse.ArgumentParser()
328
add_cleanup_args(parser)
329
330
# Run cleanup with specific parameters
331
run_cleanup(
332
file_pattern='/tmp/.ansible-runner-*',
333
exclude_strings=['important', 'keep'],
334
grace_period=60,
335
remove_images=['old-ansible-image:latest'],
336
process_isolation_executable='podman'
337
)
338
```
339
340
## Common Patterns
341
342
### Comprehensive Cleanup Workflow
343
344
```python
345
import tempfile
346
import shutil
347
from ansible_runner.utils import register_for_cleanup, cleanup_folder
348
from ansible_runner.cleanup import run_cleanup
349
350
class AnsibleWorkspace:
351
def __init__(self, base_dir=None):
352
self.base_dir = base_dir or tempfile.mkdtemp(prefix='ansible-workspace-')
353
self.temp_dirs = []
354
register_for_cleanup(self.base_dir)
355
356
def create_temp_dir(self, prefix='temp-'):
357
"""Create temporary directory within workspace"""
358
temp_dir = tempfile.mkdtemp(prefix=prefix, dir=self.base_dir)
359
self.temp_dirs.append(temp_dir)
360
return temp_dir
361
362
def cleanup_temp_dirs(self):
363
"""Clean up all temporary directories"""
364
cleaned = 0
365
for temp_dir in self.temp_dirs:
366
if cleanup_folder(temp_dir):
367
cleaned += 1
368
self.temp_dirs.clear()
369
return cleaned
370
371
def cleanup_workspace(self):
372
"""Clean up entire workspace"""
373
return cleanup_folder(self.base_dir)
374
375
# Usage
376
workspace = AnsibleWorkspace()
377
temp_dir1 = workspace.create_temp_dir('playbook-')
378
temp_dir2 = workspace.create_temp_dir('inventory-')
379
380
# Use workspace for ansible operations
381
# ... ansible-runner operations ...
382
383
# Clean up when done
384
cleaned_temps = workspace.cleanup_temp_dirs()
385
print(f"Cleaned up {cleaned_temps} temporary directories")
386
387
workspace.cleanup_workspace()
388
```
389
390
### Event Processing Pipeline
391
392
```python
393
from ansible_runner.utils import collect_new_events, ensure_str, dump_artifact
394
import json
395
import time
396
397
class EventProcessor:
398
def __init__(self, artifact_dir):
399
self.artifact_dir = artifact_dir
400
self.processed_events = []
401
self.event_stats = {}
402
403
def process_events(self):
404
"""Process new events from artifact directory"""
405
new_events = collect_new_events(self.artifact_dir, self.processed_events)
406
407
for event in new_events:
408
self._process_single_event(event)
409
self.processed_events.append(event)
410
411
return len(new_events)
412
413
def _process_single_event(self, event):
414
"""Process individual event"""
415
event_type = event.get('event', 'unknown')
416
self.event_stats[event_type] = self.event_stats.get(event_type, 0) + 1
417
418
# Convert event to string for logging
419
event_str = ensure_str(event)
420
421
# Handle specific event types
422
if event_type == 'runner_on_failed':
423
self._handle_failure(event)
424
elif event_type == 'playbook_on_stats':
425
self._handle_completion(event)
426
427
def _handle_failure(self, event):
428
"""Handle task failure events"""
429
host = event.get('event_data', {}).get('host', 'unknown')
430
task = event.get('event_data', {}).get('task', 'unknown')
431
print(f"Task failure on {host}: {task}")
432
433
def _handle_completion(self, event):
434
"""Handle playbook completion"""
435
stats = event.get('event_data', {})
436
print(f"Playbook completed with stats: {json.dumps(stats, indent=2)}")
437
438
def save_summary(self, output_path):
439
"""Save event processing summary"""
440
summary = {
441
'total_events': len(self.processed_events),
442
'event_breakdown': self.event_stats,
443
'timestamp': time.time()
444
}
445
dump_artifact(summary, output_path, 'event_summary.json')
446
447
# Usage
448
processor = EventProcessor('/tmp/ansible-artifacts')
449
450
# Process events periodically
451
while True:
452
new_count = processor.process_events()
453
if new_count > 0:
454
print(f"Processed {new_count} new events")
455
456
# Check for completion condition
457
if 'playbook_on_stats' in processor.event_stats:
458
break
459
460
time.sleep(1)
461
462
# Save final summary
463
processor.save_summary('/tmp/reports')
464
```
465
466
### Resource Management
467
468
```python
469
import os
470
import psutil
471
from ansible_runner.utils import Bunch, cleanup_folder
472
from ansible_runner.defaults import GRACE_PERIOD_DEFAULT
473
474
class ResourceManager:
475
def __init__(self):
476
self.config = Bunch(
477
max_memory_mb=1024,
478
max_cpu_percent=80,
479
cleanup_grace_period=GRACE_PERIOD_DEFAULT,
480
temp_dirs=[]
481
)
482
self.active_processes = []
483
484
def check_resource_usage(self):
485
"""Check current system resource usage"""
486
memory_mb = psutil.virtual_memory().used // (1024 * 1024)
487
cpu_percent = psutil.cpu_percent(interval=1)
488
489
return Bunch(
490
memory_mb=memory_mb,
491
cpu_percent=cpu_percent,
492
memory_ok=memory_mb < self.config.max_memory_mb,
493
cpu_ok=cpu_percent < self.config.max_cpu_percent
494
)
495
496
def create_managed_temp_dir(self, prefix='ansible-'):
497
"""Create and register temporary directory"""
498
temp_dir = tempfile.mkdtemp(prefix=prefix)
499
self.config.temp_dirs.append(temp_dir)
500
return temp_dir
501
502
def cleanup_temp_dirs(self, force=False):
503
"""Clean up managed temporary directories"""
504
cleaned = []
505
506
for temp_dir in self.config.temp_dirs[:]:
507
# Check grace period unless forced
508
if not force:
509
stat = os.stat(temp_dir)
510
age_minutes = (time.time() - stat.st_mtime) / 60
511
if age_minutes < self.config.cleanup_grace_period:
512
continue
513
514
if cleanup_folder(temp_dir):
515
cleaned.append(temp_dir)
516
self.config.temp_dirs.remove(temp_dir)
517
518
return cleaned
519
520
def get_status(self):
521
"""Get current resource manager status"""
522
resources = self.check_resource_usage()
523
return Bunch(
524
resources=resources,
525
temp_dirs_count=len(self.config.temp_dirs),
526
active_processes=len(self.active_processes)
527
)
528
529
# Usage
530
manager = ResourceManager()
531
532
# Check resources before starting
533
status = manager.get_status()
534
if not status.resources.memory_ok:
535
print("Warning: High memory usage")
536
537
# Create managed temporary directory
538
temp_dir = manager.create_managed_temp_dir('playbook-')
539
540
# ... use temp_dir for ansible operations ...
541
542
# Clean up when appropriate
543
cleaned = manager.cleanup_temp_dirs()
544
print(f"Cleaned up {len(cleaned)} temporary directories")
545
```