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

# (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 

 

from ansible import constants as C 

from ansible.errors import AnsibleParserError, AnsibleAssertionError 

from ansible.module_utils.six import string_types 

from ansible.playbook.attribute import FieldAttribute 

from ansible.playbook.base import Base 

from ansible.playbook.become import Become 

from ansible.playbook.block import Block 

from ansible.playbook.helpers import load_list_of_blocks, load_list_of_roles 

from ansible.playbook.role import Role 

from ansible.playbook.taggable import Taggable 

from ansible.vars.manager import preprocess_vars 

 

try: 

from __main__ import display 

except ImportError: 

from ansible.utils.display import Display 

display = Display() 

 

 

__all__ = ['Play'] 

 

 

class Play(Base, Taggable, Become): 

 

""" 

A play is a language feature that represents a list of roles and/or 

task/handler blocks to execute on a given set of hosts. 

 

Usage: 

 

Play.load(datastructure) -> Play 

Play.something(...) 

""" 

 

# ================================================================================= 

_hosts = FieldAttribute(isa='list', required=True, listof=string_types, always_post_validate=True) 

 

# Facts 

_fact_path = FieldAttribute(isa='string', default=None) 

_gather_facts = FieldAttribute(isa='bool', default=None, always_post_validate=True) 

_gather_subset = FieldAttribute(isa='barelist', default=None, always_post_validate=True) 

_gather_timeout = FieldAttribute(isa='int', default=None, always_post_validate=True) 

 

# Variable Attributes 

_vars_files = FieldAttribute(isa='list', default=[], priority=99) 

_vars_prompt = FieldAttribute(isa='list', default=[], always_post_validate=True) 

_vault_password = FieldAttribute(isa='string', always_post_validate=True) 

 

# Role Attributes 

_roles = FieldAttribute(isa='list', default=[], priority=90) 

 

# Block (Task) Lists Attributes 

_handlers = FieldAttribute(isa='list', default=[]) 

_pre_tasks = FieldAttribute(isa='list', default=[]) 

_post_tasks = FieldAttribute(isa='list', default=[]) 

_tasks = FieldAttribute(isa='list', default=[]) 

 

# Flag/Setting Attributes 

_force_handlers = FieldAttribute(isa='bool', always_post_validate=True) 

_max_fail_percentage = FieldAttribute(isa='percent', always_post_validate=True) 

_serial = FieldAttribute(isa='list', default=[], always_post_validate=True) 

_strategy = FieldAttribute(isa='string', default=C.DEFAULT_STRATEGY, always_post_validate=True) 

_order = FieldAttribute(isa='string', always_post_validate=True) 

 

# ================================================================================= 

 

def __init__(self): 

super(Play, self).__init__() 

 

self._included_conditional = None 

self._included_path = None 

self._removed_hosts = [] 

self.ROLE_CACHE = {} 

 

def __repr__(self): 

return self.get_name() 

 

def get_name(self): 

''' return the name of the Play ''' 

return self._attributes.get('name') 

 

@staticmethod 

def load(data, variable_manager=None, loader=None): 

105 ↛ 110line 105 didn't jump to line 110, because the condition on line 105 was never false if ('name' not in data or data['name'] is None) and 'hosts' in data: 

106 ↛ 107line 106 didn't jump to line 107, because the condition on line 106 was never true if isinstance(data['hosts'], list): 

data['name'] = ','.join(data['hosts']) 

else: 

data['name'] = data['hosts'] 

p = Play() 

return p.load_data(data, variable_manager=variable_manager, loader=loader) 

 

def preprocess_data(self, ds): 

''' 

Adjusts play datastructure to cleanup old/legacy items 

''' 

 

118 ↛ 119line 118 didn't jump to line 119, because the condition on line 118 was never true if not isinstance(ds, dict): 

raise AnsibleAssertionError('while preprocessing data (%s), ds should be a dict but was a %s' % (ds, type(ds))) 

 

# The use of 'user' in the Play datastructure was deprecated to 

# line up with the same change for Tasks, due to the fact that 

# 'user' conflicted with the user module. 

124 ↛ 127line 124 didn't jump to line 127, because the condition on line 124 was never true if 'user' in ds: 

# this should never happen, but error out with a helpful message 

# to the user if it does... 

if 'remote_user' in ds: 

raise AnsibleParserError("both 'user' and 'remote_user' are set for %s. " 

"The use of 'user' is deprecated, and should be removed" % self.get_name(), obj=ds) 

 

ds['remote_user'] = ds['user'] 

del ds['user'] 

 

return super(Play, self).preprocess_data(ds) 

 

def _load_tasks(self, attr, ds): 

''' 

Loads a list of blocks from a list which may be mixed tasks/blocks. 

Bare tasks outside of a block are given an implicit block. 

''' 

try: 

return load_list_of_blocks(ds=ds, play=self, variable_manager=self._variable_manager, loader=self._loader) 

except AssertionError as e: 

raise AnsibleParserError("A malformed block was encountered while loading tasks", obj=self._ds, orig_exc=e) 

 

def _load_pre_tasks(self, attr, ds): 

''' 

Loads a list of blocks from a list which may be mixed tasks/blocks. 

Bare tasks outside of a block are given an implicit block. 

''' 

try: 

return load_list_of_blocks(ds=ds, play=self, variable_manager=self._variable_manager, loader=self._loader) 

except AssertionError as e: 

raise AnsibleParserError("A malformed block was encountered while loading pre_tasks", obj=self._ds, orig_exc=e) 

 

def _load_post_tasks(self, attr, ds): 

''' 

Loads a list of blocks from a list which may be mixed tasks/blocks. 

Bare tasks outside of a block are given an implicit block. 

''' 

try: 

return load_list_of_blocks(ds=ds, play=self, variable_manager=self._variable_manager, loader=self._loader) 

except AssertionError as e: 

raise AnsibleParserError("A malformed block was encountered while loading post_tasks", obj=self._ds, orig_exc=e) 

 

def _load_handlers(self, attr, ds): 

''' 

Loads a list of blocks from a list which may be mixed handlers/blocks. 

Bare handlers outside of a block are given an implicit block. 

''' 

try: 

return load_list_of_blocks(ds=ds, play=self, use_handlers=True, variable_manager=self._variable_manager, loader=self._loader) 

except AssertionError as e: 

raise AnsibleParserError("A malformed block was encountered while loading handlers", obj=self._ds, orig_exc=e) 

 

def _load_roles(self, attr, ds): 

''' 

Loads and returns a list of RoleInclude objects from the datastructure 

list of role definitions and creates the Role from those objects 

''' 

 

182 ↛ 183line 182 didn't jump to line 183, because the condition on line 182 was never true if ds is None: 

ds = [] 

 

try: 

role_includes = load_list_of_roles(ds, play=self, variable_manager=self._variable_manager, loader=self._loader) 

except AssertionError as e: 

raise AnsibleParserError("A malformed role declaration was encountered.", obj=self._ds, orig_exc=e) 

 

roles = [] 

for ri in role_includes: 

roles.append(Role.load(ri, play=self)) 

return roles 

 

def _load_vars_prompt(self, attr, ds): 

new_ds = preprocess_vars(ds) 

vars_prompts = [] 

if new_ds is not None: 

for prompt_data in new_ds: 

if 'name' not in prompt_data: 

display.deprecated("Using the 'short form' for vars_prompt has been deprecated", version="2.7") 

for vname, prompt in prompt_data.items(): 

vars_prompts.append(dict( 

name=vname, 

prompt=prompt, 

default=None, 

private=None, 

confirm=None, 

encrypt=None, 

salt_size=None, 

salt=None, 

)) 

else: 

vars_prompts.append(prompt_data) 

return vars_prompts 

 

def _compile_roles(self): 

''' 

Handles the role compilation step, returning a flat list of tasks 

with the lowest level dependencies first. For example, if a role R 

has a dependency D1, which also has a dependency D2, the tasks from 

D2 are merged first, followed by D1, and lastly by the tasks from 

the parent role R last. This is done for all roles in the Play. 

''' 

 

block_list = [] 

 

228 ↛ 232line 228 didn't jump to line 232, because the condition on line 228 was never false if len(self.roles) > 0: 

for r in self.roles: 

block_list.extend(r.compile(play=self)) 

 

return block_list 

 

def compile_roles_handlers(self): 

''' 

Handles the role handler compilation step, returning a flat list of Handlers 

This is done for all roles in the Play. 

''' 

 

block_list = [] 

 

242 ↛ 246line 242 didn't jump to line 246, because the condition on line 242 was never false if len(self.roles) > 0: 

for r in self.roles: 

block_list.extend(r.get_handler_blocks(play=self)) 

 

return block_list 

 

def compile(self): 

''' 

Compiles and returns the task list for this play, compiled from the 

roles (which are themselves compiled recursively) and/or the list of 

tasks specified in the play. 

''' 

 

# create a block containing a single flush handlers meta 

# task, so we can be sure to run handlers at certain points 

# of the playbook execution 

flush_block = Block.load( 

data={'meta': 'flush_handlers'}, 

play=self, 

variable_manager=self._variable_manager, 

loader=self._loader 

) 

 

block_list = [] 

 

block_list.extend(self.pre_tasks) 

block_list.append(flush_block) 

block_list.extend(self._compile_roles()) 

block_list.extend(self.tasks) 

block_list.append(flush_block) 

block_list.extend(self.post_tasks) 

block_list.append(flush_block) 

 

return block_list 

 

def get_vars(self): 

return self.vars.copy() 

 

def get_vars_files(self): 

281 ↛ 282line 281 didn't jump to line 282, because the condition on line 281 was never true if self.vars_files is None: 

return [] 

return self.vars_files 

 

def get_handlers(self): 

return self.handlers[:] 

 

def get_roles(self): 

return self.roles[:] 

 

def get_tasks(self): 

tasklist = [] 

for task in self.pre_tasks + self.tasks + self.post_tasks: 

if isinstance(task, Block): 

tasklist.append(task.block + task.rescue + task.always) 

else: 

tasklist.append(task) 

return tasklist 

 

def serialize(self): 

data = super(Play, self).serialize() 

 

roles = [] 

for role in self.get_roles(): 

roles.append(role.serialize()) 

data['roles'] = roles 

data['included_path'] = self._included_path 

 

return data 

 

def deserialize(self, data): 

super(Play, self).deserialize(data) 

 

self._included_path = data.get('included_path', None) 

if 'roles' in data: 

role_data = data.get('roles', []) 

roles = [] 

for role in role_data: 

r = Role() 

r.deserialize(role) 

roles.append(r) 

 

setattr(self, 'roles', roles) 

del data['roles'] 

 

def copy(self): 

new_me = super(Play, self).copy() 

new_me.ROLE_CACHE = self.ROLE_CACHE.copy() 

new_me._included_conditional = self._included_conditional 

new_me._included_path = self._included_path 

return new_me