Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com> 

# 

# This file is part of Ansible 

# 

# Ansible is free software: you can redistribute it and/or modify 

# it under the terms of the GNU General Public License as published by 

# the Free Software Foundation, either version 3 of the License, or 

# (at your option) any later version. 

# 

# Ansible is distributed in the hope that it will be useful, 

# but WITHOUT ANY WARRANTY; without even the implied warranty of 

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

# GNU General Public License for more details. 

# 

# You should have received a copy of the GNU General Public License 

# along with Ansible. If not, see <http://www.gnu.org/licenses/>. 

 

# Make coding more python3-ish 

from __future__ import (absolute_import, division, print_function) 

__metaclass__ = type 

 

import fnmatch 

 

from ansible import constants as C 

from ansible.module_utils.six import iteritems 

from ansible.module_utils.parsing.convert_bool import boolean 

from ansible.playbook.block import Block 

from ansible.playbook.task import Task 

 

 

try: 

from __main__ import display 

except ImportError: 

from ansible.utils.display import Display 

display = Display() 

 

 

__all__ = ['PlayIterator'] 

 

 

class HostState: 

def __init__(self, blocks): 

self._blocks = blocks[:] 

 

self.cur_block = 0 

self.cur_regular_task = 0 

self.cur_rescue_task = 0 

self.cur_always_task = 0 

self.cur_dep_chain = None 

self.run_state = PlayIterator.ITERATING_SETUP 

self.fail_state = PlayIterator.FAILED_NONE 

self.pending_setup = False 

self.tasks_child_state = None 

self.rescue_child_state = None 

self.always_child_state = None 

self.did_rescue = False 

self.did_start_at_task = False 

 

def __repr__(self): 

return "HostState(%r)" % self._blocks 

 

def __str__(self): 

def _run_state_to_string(n): 

states = ["ITERATING_SETUP", "ITERATING_TASKS", "ITERATING_RESCUE", "ITERATING_ALWAYS", "ITERATING_COMPLETE"] 

try: 

return states[n] 

except IndexError: 

return "UNKNOWN STATE" 

 

def _failed_state_to_string(n): 

states = {1: "FAILED_SETUP", 2: "FAILED_TASKS", 4: "FAILED_RESCUE", 8: "FAILED_ALWAYS"} 

72 ↛ 75line 72 didn't jump to line 75, because the condition on line 72 was never false if n == 0: 

return "FAILED_NONE" 

else: 

ret = [] 

for i in (1, 2, 4, 8): 

if n & i: 

ret.append(states[i]) 

return "|".join(ret) 

 

return ("HOST STATE: block=%d, task=%d, rescue=%d, always=%d, run_state=%s, fail_state=%s, pending_setup=%s, tasks child state? (%s), " 

"rescue child state? (%s), always child state? (%s), did rescue? %s, did start at task? %s" % ( 

self.cur_block, 

self.cur_regular_task, 

self.cur_rescue_task, 

self.cur_always_task, 

_run_state_to_string(self.run_state), 

_failed_state_to_string(self.fail_state), 

self.pending_setup, 

self.tasks_child_state, 

self.rescue_child_state, 

self.always_child_state, 

self.did_rescue, 

self.did_start_at_task, 

)) 

 

def __eq__(self, other): 

if not isinstance(other, HostState): 

return False 

 

for attr in ('_blocks', 'cur_block', 'cur_regular_task', 'cur_rescue_task', 'cur_always_task', 

'run_state', 'fail_state', 'pending_setup', 'cur_dep_chain', 

'tasks_child_state', 'rescue_child_state', 'always_child_state'): 

if getattr(self, attr) != getattr(other, attr): 

return False 

 

return True 

 

def get_current_block(self): 

return self._blocks[self.cur_block] 

 

def copy(self): 

new_state = HostState(self._blocks) 

new_state.cur_block = self.cur_block 

new_state.cur_regular_task = self.cur_regular_task 

new_state.cur_rescue_task = self.cur_rescue_task 

new_state.cur_always_task = self.cur_always_task 

new_state.run_state = self.run_state 

new_state.fail_state = self.fail_state 

new_state.pending_setup = self.pending_setup 

new_state.did_rescue = self.did_rescue 

new_state.did_start_at_task = self.did_start_at_task 

123 ↛ 124line 123 didn't jump to line 124, because the condition on line 123 was never true if self.cur_dep_chain is not None: 

new_state.cur_dep_chain = self.cur_dep_chain[:] 

if self.tasks_child_state is not None: 

new_state.tasks_child_state = self.tasks_child_state.copy() 

127 ↛ 128line 127 didn't jump to line 128, because the condition on line 127 was never true if self.rescue_child_state is not None: 

new_state.rescue_child_state = self.rescue_child_state.copy() 

129 ↛ 130line 129 didn't jump to line 130, because the condition on line 129 was never true if self.always_child_state is not None: 

new_state.always_child_state = self.always_child_state.copy() 

return new_state 

 

 

class PlayIterator: 

 

# the primary running states for the play iteration 

ITERATING_SETUP = 0 

ITERATING_TASKS = 1 

ITERATING_RESCUE = 2 

ITERATING_ALWAYS = 3 

ITERATING_COMPLETE = 4 

 

# the failure states for the play iteration, which are powers 

# of 2 as they may be or'ed together in certain circumstances 

FAILED_NONE = 0 

FAILED_SETUP = 1 

FAILED_TASKS = 2 

FAILED_RESCUE = 4 

FAILED_ALWAYS = 8 

 

def __init__(self, inventory, play, play_context, variable_manager, all_vars, start_at_done=False): 

self._play = play 

self._blocks = [] 

self._variable_manager = variable_manager 

 

# Default options to gather 

gather_subset = play_context.gather_subset 

gather_timeout = play_context.gather_timeout 

fact_path = play_context.fact_path 

 

# Retrieve subset to gather 

162 ↛ 163line 162 didn't jump to line 163, because the condition on line 162 was never true if self._play.gather_subset is not None: 

gather_subset = self._play.gather_subset 

# Retrieve timeout for gather 

165 ↛ 166line 165 didn't jump to line 166, because the condition on line 165 was never true if self._play.gather_timeout is not None: 

gather_timeout = self._play.gather_timeout 

# Retrieve fact_path 

168 ↛ 169line 168 didn't jump to line 169, because the condition on line 168 was never true if self._play.fact_path is not None: 

fact_path = self._play.fact_path 

 

setup_block = Block(play=self._play) 

setup_task = Task(block=setup_block) 

setup_task.action = 'setup' 

setup_task.name = 'Gathering Facts' 

setup_task.tags = ['always'] 

setup_task.args = { 

'gather_subset': gather_subset, 

} 

179 ↛ 181line 179 didn't jump to line 181, because the condition on line 179 was never false if gather_timeout: 

setup_task.args['gather_timeout'] = gather_timeout 

181 ↛ 182line 181 didn't jump to line 182, because the condition on line 181 was never true if fact_path: 

setup_task.args['fact_path'] = fact_path 

setup_task.set_loader(self._play._loader) 

# short circuit fact gathering if the entire playbook is conditional 

185 ↛ 186line 185 didn't jump to line 186, because the condition on line 185 was never true if self._play._included_conditional is not None: 

setup_task.when = self._play._included_conditional[:] 

setup_block.block = [setup_task] 

 

setup_block = setup_block.filter_tagged_tasks(play_context, all_vars) 

self._blocks.append(setup_block) 

self.cache_block_tasks(setup_block) 

 

for block in self._play.compile(): 

new_block = block.filter_tagged_tasks(play_context, all_vars) 

195 ↛ 193line 195 didn't jump to line 193, because the condition on line 195 was never false if new_block.has_tasks(): 

self.cache_block_tasks(new_block) 

self._blocks.append(new_block) 

 

199 ↛ 200line 199 didn't jump to line 200, because the loop on line 199 never started for handler_block in self._play.handlers: 

self.cache_block_tasks(handler_block) 

 

self._host_states = {} 

start_at_matched = False 

batch = inventory.get_hosts(self._play.hosts) 

self.batch_size = len(batch) 

for host in batch: 

self._host_states[host.name] = HostState(blocks=self._blocks) 

# if we're looking to start at a specific task, iterate through 

# the tasks for this host until we find the specified task 

210 ↛ 211line 210 didn't jump to line 211, because the condition on line 210 was never true if play_context.start_at_task is not None and not start_at_done: 

while True: 

(s, task) = self.get_next_task_for_host(host, peek=True) 

if s.run_state == self.ITERATING_COMPLETE: 

break 

if task.name == play_context.start_at_task or fnmatch.fnmatch(task.name, play_context.start_at_task) or \ 

task.get_name() == play_context.start_at_task or fnmatch.fnmatch(task.get_name(), play_context.start_at_task): 

start_at_matched = True 

break 

else: 

self.get_next_task_for_host(host) 

 

# finally, reset the host's state to ITERATING_SETUP 

if start_at_matched: 

self._host_states[host.name].did_start_at_task = True 

self._host_states[host.name].run_state = self.ITERATING_SETUP 

 

227 ↛ 231line 227 didn't jump to line 231, because the condition on line 227 was never true if start_at_matched: 

# we have our match, so clear the start_at_task field on the 

# play context to flag that we've started at a task (and future 

# plays won't try to advance) 

play_context.start_at_task = None 

 

def get_host_state(self, host): 

# Since we're using the PlayIterator to carry forward failed hosts, 

# in the event that a previous host was not in the current inventory 

# we create a stub state for it now 

237 ↛ 238line 237 didn't jump to line 238, because the condition on line 237 was never true if host.name not in self._host_states: 

self._host_states[host.name] = HostState(blocks=[]) 

 

return self._host_states[host.name].copy() 

 

def cache_block_tasks(self, block): 

# now a noop, we've changed the way we do caching and finding of 

# original task entries, but just in case any 3rd party strategies 

# are using this we're leaving it here for now 

return 

 

def get_next_task_for_host(self, host, peek=False): 

 

display.debug("getting the next task for host %s" % host.name) 

s = self.get_host_state(host) 

 

task = None 

254 ↛ 255line 254 didn't jump to line 255, because the condition on line 254 was never true if s.run_state == self.ITERATING_COMPLETE: 

display.debug("host %s is done iterating, returning" % host.name) 

return (s, None) 

 

(s, task) = self._get_next_task_from_state(s, host=host, peek=peek) 

 

if not peek: 

self._host_states[host.name] = s 

 

display.debug("done getting next task for host %s" % host.name) 

display.debug(" ^ task is: %s" % task) 

display.debug(" ^ state is: %s" % s) 

return (s, task) 

 

def _get_next_task_from_state(self, state, host, peek, in_child=False): 

 

task = None 

 

# try and find the next task, given the current state. 

while True: 

# try to get the current block from the list of blocks, and 

# if we run past the end of the list we know we're done with 

# this block 

try: 

block = state._blocks[state.cur_block] 

except IndexError: 

state.run_state = self.ITERATING_COMPLETE 

return (state, None) 

 

if state.run_state == self.ITERATING_SETUP: 

# First, we check to see if we were pending setup. If not, this is 

# the first trip through ITERATING_SETUP, so we set the pending_setup 

# flag and try to determine if we do in fact want to gather facts for 

# the specified host. 

if not state.pending_setup: 

state.pending_setup = True 

 

# Gather facts if the default is 'smart' and we have not yet 

# done it for this host; or if 'explicit' and the play sets 

# gather_facts to True; or if 'implicit' and the play does 

# NOT explicitly set gather_facts to False. 

 

gathering = C.DEFAULT_GATHERING 

implied = self._play.gather_facts is None or boolean(self._play.gather_facts, strict=False) 

 

299 ↛ 304line 299 didn't jump to line 304, because the condition on line 299 was never true if (gathering == 'implicit' and implied) or \ 

(gathering == 'explicit' and boolean(self._play.gather_facts, strict=False)) or \ 

(gathering == 'smart' and implied and not (self._variable_manager._fact_cache.get(host.name, {}).get('module_setup', False))): 

# The setup block is always self._blocks[0], as we inject it 

# during the play compilation in __init__ above. 

setup_block = self._blocks[0] 

if setup_block.has_tasks() and len(setup_block.block) > 0: 

task = setup_block.block[0] 

else: 

# This is the second trip through ITERATING_SETUP, so we clear 

# the flag and move onto the next block in the list while setting 

# the run state to ITERATING_TASKS 

state.pending_setup = False 

 

state.run_state = self.ITERATING_TASKS 

314 ↛ 440line 314 didn't jump to line 440, because the condition on line 314 was never false if not state.did_start_at_task: 

state.cur_block += 1 

state.cur_regular_task = 0 

state.cur_rescue_task = 0 

state.cur_always_task = 0 

state.child_state = None 

 

elif state.run_state == self.ITERATING_TASKS: 

# clear the pending setup flag, since we're past that and it didn't fail 

323 ↛ 324line 323 didn't jump to line 324, because the condition on line 323 was never true if state.pending_setup: 

state.pending_setup = False 

 

# First, we check for a child task state that is not failed, and if we 

# have one recurse into it for the next task. If we're done with the child 

# state, we clear it and drop back to getting the next task from the list. 

if state.tasks_child_state: 

(state.tasks_child_state, task) = self._get_next_task_from_state(state.tasks_child_state, host=host, peek=peek, in_child=True) 

331 ↛ 333line 331 didn't jump to line 333, because the condition on line 331 was never true if self._check_failed_state(state.tasks_child_state): 

# failed child state, so clear it and move into the rescue portion 

state.tasks_child_state = None 

self._set_failed_state(state) 

else: 

# get the next task recursively 

if task is None or state.tasks_child_state.run_state == self.ITERATING_COMPLETE: 

# we're done with the child state, so clear it and continue 

# back to the top of the loop to get the next task 

state.tasks_child_state = None 

continue 

else: 

# First here, we check to see if we've failed anywhere down the chain 

# of states we have, and if so we move onto the rescue portion. Otherwise, 

# we check to see if we've moved past the end of the list of tasks. If so, 

# we move into the always portion of the block, otherwise we get the next 

# task from the list. 

348 ↛ 349line 348 didn't jump to line 349, because the condition on line 348 was never true if self._check_failed_state(state): 

state.run_state = self.ITERATING_RESCUE 

elif state.cur_regular_task >= len(block.block): 

state.run_state = self.ITERATING_ALWAYS 

else: 

task = block.block[state.cur_regular_task] 

# if the current task is actually a child block, create a child 

# state for us to recurse into on the next pass 

if isinstance(task, Block) or state.tasks_child_state is not None: 

state.tasks_child_state = HostState(blocks=[task]) 

state.tasks_child_state.run_state = self.ITERATING_TASKS 

# since we've created the child state, clear the task 

# so we can pick up the child state on the next pass 

task = None 

state.cur_regular_task += 1 

 

364 ↛ 367line 364 didn't jump to line 367, because the condition on line 364 was never true elif state.run_state == self.ITERATING_RESCUE: 

# The process here is identical to ITERATING_TASKS, except instead 

# we move into the always portion of the block. 

if host.name in self._play._removed_hosts: 

self._play._removed_hosts.remove(host.name) 

 

if state.rescue_child_state: 

(state.rescue_child_state, task) = self._get_next_task_from_state(state.rescue_child_state, host=host, peek=peek, in_child=True) 

if self._check_failed_state(state.rescue_child_state): 

state.rescue_child_state = None 

self._set_failed_state(state) 

else: 

if task is None or state.rescue_child_state.run_state == self.ITERATING_COMPLETE: 

state.rescue_child_state = None 

continue 

else: 

if state.fail_state & self.FAILED_RESCUE == self.FAILED_RESCUE: 

state.run_state = self.ITERATING_ALWAYS 

elif state.cur_rescue_task >= len(block.rescue): 

if len(block.rescue) > 0: 

state.fail_state = self.FAILED_NONE 

state.run_state = self.ITERATING_ALWAYS 

state.did_rescue = True 

else: 

task = block.rescue[state.cur_rescue_task] 

if isinstance(task, Block) or state.rescue_child_state is not None: 

state.rescue_child_state = HostState(blocks=[task]) 

state.rescue_child_state.run_state = self.ITERATING_TASKS 

task = None 

state.cur_rescue_task += 1 

 

395 ↛ 436line 395 didn't jump to line 436, because the condition on line 395 was never false elif state.run_state == self.ITERATING_ALWAYS: 

# And again, the process here is identical to ITERATING_TASKS, except 

# instead we either move onto the next block in the list, or we set the 

# run state to ITERATING_COMPLETE in the event of any errors, or when we 

# have hit the end of the list of blocks. 

400 ↛ 401line 400 didn't jump to line 401, because the condition on line 400 was never true if state.always_child_state: 

(state.always_child_state, task) = self._get_next_task_from_state(state.always_child_state, host=host, peek=peek, in_child=True) 

if self._check_failed_state(state.always_child_state): 

state.always_child_state = None 

self._set_failed_state(state) 

else: 

if task is None or state.always_child_state.run_state == self.ITERATING_COMPLETE: 

state.always_child_state = None 

continue 

else: 

if state.cur_always_task >= len(block.always): 

411 ↛ 412line 411 didn't jump to line 412, because the condition on line 411 was never true if state.fail_state != self.FAILED_NONE: 

state.run_state = self.ITERATING_COMPLETE 

else: 

state.cur_block += 1 

state.cur_regular_task = 0 

state.cur_rescue_task = 0 

state.cur_always_task = 0 

state.run_state = self.ITERATING_TASKS 

state.tasks_child_state = None 

state.rescue_child_state = None 

state.always_child_state = None 

state.did_rescue = False 

 

# we're advancing blocks, so if this was an end-of-role block we 

# mark the current role complete 

if block._eor and host.name in block._role._had_task_run and not in_child and not peek: 

block._role._completed[host.name] = True 

else: 

task = block.always[state.cur_always_task] 

430 ↛ 431line 430 didn't jump to line 431, because the condition on line 430 was never true if isinstance(task, Block) or state.always_child_state is not None: 

state.always_child_state = HostState(blocks=[task]) 

state.always_child_state.run_state = self.ITERATING_TASKS 

task = None 

state.cur_always_task += 1 

 

elif state.run_state == self.ITERATING_COMPLETE: 

return (state, None) 

 

# if something above set the task, break out of the loop now 

if task: 

break 

 

return (state, task) 

 

def _set_failed_state(self, state): 

if state.run_state == self.ITERATING_SETUP: 

state.fail_state |= self.FAILED_SETUP 

state.run_state = self.ITERATING_COMPLETE 

elif state.run_state == self.ITERATING_TASKS: 

if state.tasks_child_state is not None: 

state.tasks_child_state = self._set_failed_state(state.tasks_child_state) 

else: 

state.fail_state |= self.FAILED_TASKS 

if state._blocks[state.cur_block].rescue: 

state.run_state = self.ITERATING_RESCUE 

elif state._blocks[state.cur_block].always: 

state.run_state = self.ITERATING_ALWAYS 

else: 

state.run_state = self.ITERATING_COMPLETE 

elif state.run_state == self.ITERATING_RESCUE: 

if state.rescue_child_state is not None: 

state.rescue_child_state = self._set_failed_state(state.rescue_child_state) 

else: 

state.fail_state |= self.FAILED_RESCUE 

if state._blocks[state.cur_block].always: 

state.run_state = self.ITERATING_ALWAYS 

else: 

state.run_state = self.ITERATING_COMPLETE 

elif state.run_state == self.ITERATING_ALWAYS: 

if state.always_child_state is not None: 

state.always_child_state = self._set_failed_state(state.always_child_state) 

else: 

state.fail_state |= self.FAILED_ALWAYS 

state.run_state = self.ITERATING_COMPLETE 

return state 

 

def mark_host_failed(self, host): 

s = self.get_host_state(host) 

display.debug("marking host %s failed, current state: %s" % (host, s)) 

s = self._set_failed_state(s) 

display.debug("^ failed state is now: %s" % s) 

self._host_states[host.name] = s 

self._play._removed_hosts.append(host.name) 

 

def get_failed_hosts(self): 

return dict((host, True) for (host, state) in iteritems(self._host_states) if self._check_failed_state(state)) 

 

def _check_failed_state(self, state): 

if state is None: 

return False 

491 ↛ 492line 491 didn't jump to line 492, because the condition on line 491 was never true elif state.run_state == self.ITERATING_RESCUE and self._check_failed_state(state.rescue_child_state): 

return True 

493 ↛ 494line 493 didn't jump to line 494, because the condition on line 493 was never true elif state.run_state == self.ITERATING_ALWAYS and self._check_failed_state(state.always_child_state): 

return True 

495 ↛ 496line 495 didn't jump to line 496, because the condition on line 495 was never true elif state.fail_state != self.FAILED_NONE: 

if state.run_state == self.ITERATING_RESCUE and state.fail_state & self.FAILED_RESCUE == 0: 

return False 

elif state.run_state == self.ITERATING_ALWAYS and state.fail_state & self.FAILED_ALWAYS == 0: 

return False 

else: 

return not state.did_rescue 

502 ↛ 503line 502 didn't jump to line 503, because the condition on line 502 was never true elif state.run_state == self.ITERATING_TASKS and self._check_failed_state(state.tasks_child_state): 

cur_block = self._blocks[state.cur_block] 

if len(cur_block.rescue) > 0 and state.fail_state & self.FAILED_RESCUE == 0: 

return False 

else: 

return True 

return False 

 

def is_failed(self, host): 

s = self.get_host_state(host) 

return self._check_failed_state(s) 

 

def get_original_task(self, host, task): 

# now a noop because we've changed the way we do caching 

return (None, None) 

 

def _insert_tasks_into_state(self, state, task_list): 

# if we've failed at all, or if the task list is empty, just return the current state 

520 ↛ 521line 520 didn't jump to line 521, because the condition on line 520 was never true if state.fail_state != self.FAILED_NONE and state.run_state not in (self.ITERATING_RESCUE, self.ITERATING_ALWAYS) or not task_list: 

return state 

 

523 ↛ 532line 523 didn't jump to line 532, because the condition on line 523 was never false if state.run_state == self.ITERATING_TASKS: 

if state.tasks_child_state: 

state.tasks_child_state = self._insert_tasks_into_state(state.tasks_child_state, task_list) 

else: 

target_block = state._blocks[state.cur_block].copy(exclude_parent=True) 

before = target_block.block[:state.cur_regular_task] 

after = target_block.block[state.cur_regular_task:] 

target_block.block = before + task_list + after 

state._blocks[state.cur_block] = target_block 

elif state.run_state == self.ITERATING_RESCUE: 

if state.rescue_child_state: 

state.rescue_child_state = self._insert_tasks_into_state(state.rescue_child_state, task_list) 

else: 

target_block = state._blocks[state.cur_block].copy(exclude_parent=True) 

before = target_block.rescue[:state.cur_rescue_task] 

after = target_block.rescue[state.cur_rescue_task:] 

target_block.rescue = before + task_list + after 

state._blocks[state.cur_block] = target_block 

elif state.run_state == self.ITERATING_ALWAYS: 

if state.always_child_state: 

state.always_child_state = self._insert_tasks_into_state(state.always_child_state, task_list) 

else: 

target_block = state._blocks[state.cur_block].copy(exclude_parent=True) 

before = target_block.always[:state.cur_always_task] 

after = target_block.always[state.cur_always_task:] 

target_block.always = before + task_list + after 

state._blocks[state.cur_block] = target_block 

return state 

 

def add_tasks(self, host, task_list): 

self._host_states[host.name] = self._insert_tasks_into_state(self.get_host_state(host), task_list)