or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

command-execution.mdconfiguration-runner.mdindex.mdplaybook-execution.mdplugin-role-management.mdstreaming-distributed.mdutilities-cleanup.md

utilities-cleanup.mddocs/

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

```