mirror of
https://github.com/ansible/ansible.git
synced 2026-06-08 16:34:06 -04:00
Optimize playbook objects copying
ci_complete
This commit is contained in:
parent
ba21909655
commit
5cde456833
11 changed files with 73 additions and 148 deletions
|
|
@ -202,8 +202,7 @@ class TaskContext(AmbientContextBase):
|
|||
original_task = self.task
|
||||
original_play_context = te._play_context
|
||||
|
||||
loop_item_task = self.task.copy(exclude_parent=True, exclude_tasks=True)
|
||||
loop_item_task._parent = self.task._parent
|
||||
loop_item_task = self.task.copy()
|
||||
loop_item_play_context = te._play_context.copy()
|
||||
|
||||
self._task = loop_item_task
|
||||
|
|
|
|||
|
|
@ -603,23 +603,36 @@ class PlayIterator:
|
|||
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_tasks=True)
|
||||
target_block.block[state.cur_regular_task:state.cur_regular_task] = task_list
|
||||
state._blocks[state.cur_block] = target_block
|
||||
cur_block = state._blocks[state.cur_block]
|
||||
if cur_block._copied:
|
||||
cur_block.block[state.cur_regular_task:state.cur_regular_task] = task_list
|
||||
else:
|
||||
target_block = cur_block.copy()
|
||||
target_block.block[state.cur_regular_task:state.cur_regular_task] = task_list
|
||||
state._blocks[state.cur_block] = target_block
|
||||
elif state.run_state == IteratingStates.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_tasks=True)
|
||||
target_block.rescue[state.cur_rescue_task:state.cur_rescue_task] = task_list
|
||||
state._blocks[state.cur_block] = target_block
|
||||
cur_block = state._blocks[state.cur_block]
|
||||
if cur_block._copied:
|
||||
cur_block.rescue[state.cur_rescue_task:state.cur_rescue_task] = task_list
|
||||
else:
|
||||
target_block = cur_block.copy()
|
||||
target_block.rescue[state.cur_rescue_task:state.cur_rescue_task] = task_list
|
||||
state._blocks[state.cur_block] = target_block
|
||||
elif state.run_state == IteratingStates.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_tasks=True)
|
||||
target_block.always[state.cur_always_task:state.cur_always_task] = task_list
|
||||
state._blocks[state.cur_block] = target_block
|
||||
cur_block = state._blocks[state.cur_block]
|
||||
if cur_block._copied:
|
||||
cur_block.rescue[state.cur_rescue_task:state.cur_rescue_task] = task_list
|
||||
cur_block.always[state.cur_always_task:state.cur_always_task] = task_list
|
||||
else:
|
||||
target_block = cur_block.copy()
|
||||
target_block.always[state.cur_always_task:state.cur_always_task] = task_list
|
||||
state._blocks[state.cur_block] = target_block
|
||||
elif state.run_state == IteratingStates.HANDLERS:
|
||||
state.handlers[state.cur_handlers_task:state.cur_handlers_task] = [h for b in task_list for h in b.block]
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ import itertools
|
|||
import operator
|
||||
import os
|
||||
|
||||
import copy
|
||||
import typing as t
|
||||
|
||||
from copy import copy as shallowcopy
|
||||
from functools import cache
|
||||
|
||||
from ansible import constants as C
|
||||
|
|
@ -423,29 +423,12 @@ class FieldAttributeBase:
|
|||
self._squashed = True
|
||||
|
||||
def copy(self):
|
||||
"""
|
||||
Create a copy of this object and return it.
|
||||
"""
|
||||
|
||||
try:
|
||||
new_me = self.__class__()
|
||||
except RecursionError as ex:
|
||||
raise AnsibleError("Exceeded maximum object depth. This may have been caused by excessive role recursion.") from ex
|
||||
|
||||
new_me = copy.copy(self)
|
||||
nd = new_me.__dict__
|
||||
for name in self.fattributes:
|
||||
setattr(new_me, name, shallowcopy(getattr(self, f'_{name}', Sentinel)))
|
||||
|
||||
new_me._loader = self._loader
|
||||
new_me._variable_manager = self._variable_manager
|
||||
new_me._origin = self._origin
|
||||
new_me._validated = self._validated
|
||||
new_me._finalized = self._finalized
|
||||
new_me._uuid = self._uuid
|
||||
|
||||
# if the ds value was set on the object, copy it to the new copy too
|
||||
if hasattr(self, '_ds'):
|
||||
new_me._ds = self._ds
|
||||
|
||||
n = f'_{name}'
|
||||
if (v := nd.get(n)) is not None and isinstance(v, (list, dict)):
|
||||
nd[n] = v.copy()
|
||||
return new_me
|
||||
|
||||
def get_validated_value(self, name, attribute, value, templar):
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import itertools
|
||||
|
||||
from ansible.errors import AnsibleParserError
|
||||
from ansible.module_utils.common.sentinel import Sentinel
|
||||
from ansible.playbook.attribute import NonInheritableFieldAttribute
|
||||
|
|
@ -40,13 +42,13 @@ class Block(Base, Conditional, CollectionSearch, Taggable, Notifiable, Delegatab
|
|||
# similar to the 'else' clause for exceptions
|
||||
# otherwise = FieldAttribute(isa='list')
|
||||
|
||||
def __init__(self, play=None, parent_block=None, role=None, task_include=None, use_handlers=False, implicit=False):
|
||||
def __init__(self, play=None, parent_block=None, role=None, task_include=None, use_handlers=False):
|
||||
self._play = play
|
||||
self._role = role
|
||||
self._parent = None
|
||||
self._dep_chain = None
|
||||
self._use_handlers = use_handlers
|
||||
self._implicit = implicit
|
||||
self._copied = False
|
||||
|
||||
if task_include:
|
||||
self._parent = task_include
|
||||
|
|
@ -83,8 +85,7 @@ class Block(Base, Conditional, CollectionSearch, Taggable, Notifiable, Delegatab
|
|||
|
||||
@staticmethod
|
||||
def load(data, play=None, parent_block=None, role=None, task_include=None, use_handlers=False, variable_manager=None, loader=None):
|
||||
implicit = not Block.is_block(data)
|
||||
b = Block(play=play, parent_block=parent_block, role=role, task_include=task_include, use_handlers=use_handlers, implicit=implicit)
|
||||
b = Block(play=play, parent_block=parent_block, role=role, task_include=task_include, use_handlers=use_handlers)
|
||||
return b.load_data(data, variable_manager=variable_manager, loader=loader)
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -150,49 +151,32 @@ class Block(Base, Conditional, CollectionSearch, Taggable, Notifiable, Delegatab
|
|||
else:
|
||||
return self._dep_chain[:]
|
||||
|
||||
def copy(self, exclude_parent=False, exclude_tasks=False):
|
||||
def _dupe_task_list(task_list, new_block):
|
||||
def copy(self):
|
||||
def _reparent_tasks(task_list, new_block):
|
||||
new_task_list = []
|
||||
for task in task_list:
|
||||
new_task = task.copy(exclude_parent=True, exclude_tasks=exclude_tasks)
|
||||
if task._parent:
|
||||
new_task._parent = task._parent.copy(exclude_tasks=True)
|
||||
if task._parent == new_block:
|
||||
# If task._parent is the same as new_block, just replace it
|
||||
new_task._parent = new_block
|
||||
else:
|
||||
# task may not be a direct child of new_block, search for the correct place to insert new_block
|
||||
cur_obj = new_task._parent
|
||||
while cur_obj._parent and cur_obj._parent != new_block:
|
||||
cur_obj = cur_obj._parent
|
||||
|
||||
cur_obj._parent = new_block
|
||||
else:
|
||||
new_task = task.copy()
|
||||
if task._parent == new_block:
|
||||
# If task._parent is the same as new_block, just replace it
|
||||
new_task._parent = new_block
|
||||
else:
|
||||
# parent is include/import, skip one level
|
||||
new_task._parent._parent = new_block
|
||||
new_task_list.append(new_task)
|
||||
return new_task_list
|
||||
|
||||
new_me = super(Block, self).copy()
|
||||
new_me._play = self._play
|
||||
new_me._use_handlers = self._use_handlers
|
||||
new_me = super().copy()
|
||||
|
||||
if self._dep_chain is not None:
|
||||
new_me._dep_chain = self._dep_chain[:]
|
||||
|
||||
new_me._parent = None
|
||||
if self._parent and not exclude_parent:
|
||||
new_me._parent = self._parent.copy(exclude_tasks=True)
|
||||
# re-parent tasks within the block to point at the new one via _parent
|
||||
new_me.block = _reparent_tasks(self.block, new_me)
|
||||
new_me.rescue = _reparent_tasks(self.rescue, new_me)
|
||||
new_me.always = _reparent_tasks(self.always, new_me)
|
||||
|
||||
if not exclude_tasks:
|
||||
new_me.block = _dupe_task_list(self.block or [], new_me)
|
||||
new_me.rescue = _dupe_task_list(self.rescue or [], new_me)
|
||||
new_me.always = _dupe_task_list(self.always or [], new_me)
|
||||
new_me._copied = True
|
||||
|
||||
new_me._role = None
|
||||
if self._role:
|
||||
new_me._role = self._role
|
||||
|
||||
new_me.validate()
|
||||
return new_me
|
||||
|
||||
def set_loader(self, loader):
|
||||
|
|
@ -289,40 +273,26 @@ class Block(Base, Conditional, CollectionSearch, Taggable, Notifiable, Delegatab
|
|||
tmp_list = []
|
||||
for task in target:
|
||||
if isinstance(task, Block):
|
||||
filtered_block = evaluate_block(task)
|
||||
filtered_block = task.filter_tagged_tasks(all_vars)
|
||||
if filtered_block.has_tasks():
|
||||
tmp_list.append(filtered_block)
|
||||
elif task.evaluate_tags(self._play.only_tags, self._play.skip_tags, all_vars=all_vars):
|
||||
tmp_list.append(task)
|
||||
return tmp_list
|
||||
|
||||
def evaluate_block(block):
|
||||
new_block = block.copy(exclude_parent=True, exclude_tasks=True)
|
||||
new_block._parent = block._parent
|
||||
new_block.block = evaluate_and_append_task(block.block)
|
||||
new_block.rescue = evaluate_and_append_task(block.rescue)
|
||||
new_block.always = evaluate_and_append_task(block.always)
|
||||
return new_block
|
||||
|
||||
return evaluate_block(self)
|
||||
self.block = evaluate_and_append_task(self.block)
|
||||
self.rescue = evaluate_and_append_task(self.rescue)
|
||||
self.always = evaluate_and_append_task(self.always)
|
||||
return self # FIXME
|
||||
|
||||
def get_tasks(self):
|
||||
def evaluate_and_append_task(target):
|
||||
tmp_list = []
|
||||
for task in target:
|
||||
if isinstance(task, Block):
|
||||
tmp_list.extend(evaluate_block(task))
|
||||
else:
|
||||
tmp_list.append(task)
|
||||
return tmp_list
|
||||
|
||||
def evaluate_block(block):
|
||||
rv = evaluate_and_append_task(block.block)
|
||||
rv.extend(evaluate_and_append_task(block.rescue))
|
||||
rv.extend(evaluate_and_append_task(block.always))
|
||||
return rv
|
||||
|
||||
return evaluate_block(self)
|
||||
task_list = []
|
||||
for task in itertools.chain(self.block, self.rescue, self.always):
|
||||
if isinstance(task, Block):
|
||||
task_list.extend(task.get_tasks())
|
||||
else:
|
||||
task_list.append(task)
|
||||
return task_list
|
||||
|
||||
def has_tasks(self):
|
||||
return len(self.block) > 0 or len(self.rescue) > 0 or len(self.always) > 0
|
||||
|
|
|
|||
|
|
@ -219,13 +219,11 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h
|
|||
# nested includes, and we want the include order printed correctly
|
||||
display.vv("statically imported: %s" % include_file)
|
||||
|
||||
ti_copy = task.copy(exclude_parent=True)
|
||||
ti_copy._parent = block
|
||||
included_blocks = load_list_of_blocks(
|
||||
data,
|
||||
play=play,
|
||||
parent_block=None,
|
||||
task_include=ti_copy,
|
||||
task_include=task,
|
||||
role=role,
|
||||
use_handlers=use_handlers,
|
||||
loader=loader,
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ class Play(Base, Taggable, CollectionSearch):
|
|||
if self.pre_tasks:
|
||||
b.block = self.pre_tasks
|
||||
else:
|
||||
nt = noop_task.copy(exclude_parent=True)
|
||||
nt = noop_task.copy()
|
||||
nt._parent = b
|
||||
b.block = [nt]
|
||||
b.always = [flush_block]
|
||||
|
|
@ -331,7 +331,7 @@ class Play(Base, Taggable, CollectionSearch):
|
|||
if tasks:
|
||||
b.block = tasks
|
||||
else:
|
||||
nt = noop_task.copy(exclude_parent=True)
|
||||
nt = noop_task.copy()
|
||||
nt._parent = b
|
||||
b.block = [nt]
|
||||
b.always = [flush_block]
|
||||
|
|
@ -341,7 +341,7 @@ class Play(Base, Taggable, CollectionSearch):
|
|||
if self.post_tasks:
|
||||
b.block = self.post_tasks
|
||||
else:
|
||||
nt = noop_task.copy(exclude_parent=True)
|
||||
nt = noop_task.copy()
|
||||
nt._parent = b
|
||||
b.block = [nt]
|
||||
b.always = [flush_block]
|
||||
|
|
@ -385,12 +385,8 @@ class Play(Base, Taggable, CollectionSearch):
|
|||
return tasklist
|
||||
|
||||
def copy(self):
|
||||
new_me = super(Play, self).copy()
|
||||
new_me = super().copy()
|
||||
new_me.role_cache = self.role_cache.copy()
|
||||
new_me._included_conditional = self._included_conditional
|
||||
new_me._included_path = self._included_path
|
||||
new_me._action_groups = self._action_groups
|
||||
new_me._group_actions = self._group_actions
|
||||
return new_me
|
||||
|
||||
def _post_validate_validate_argspec(self, attr: NonInheritableFieldAttribute, value: object, templar: _TE) -> str | None:
|
||||
|
|
|
|||
|
|
@ -601,10 +601,8 @@ class Role(Base, Conditional, Taggable, CollectionSearch, Delegatable):
|
|||
block_list.extend(dep_blocks)
|
||||
|
||||
for task_block in self._handler_blocks:
|
||||
new_task_block = task_block.copy()
|
||||
new_task_block._dep_chain = new_dep_chain
|
||||
new_task_block._play = play
|
||||
block_list.append(new_task_block)
|
||||
task_block._dep_chain = new_dep_chain
|
||||
block_list.append(task_block)
|
||||
|
||||
return block_list
|
||||
|
||||
|
|
@ -642,10 +640,8 @@ class Role(Base, Conditional, Taggable, CollectionSearch, Delegatable):
|
|||
block_list.extend(dep_blocks)
|
||||
|
||||
for task_block in self._task_blocks:
|
||||
new_task_block = task_block.copy()
|
||||
new_task_block._dep_chain = new_dep_chain
|
||||
new_task_block._play = play
|
||||
block_list.append(new_task_block)
|
||||
task_block._dep_chain = new_dep_chain
|
||||
block_list.append(task_block)
|
||||
|
||||
eor_block = Block(play=play)
|
||||
eor_block._loader = self._loader
|
||||
|
|
|
|||
|
|
@ -164,15 +164,9 @@ class IncludeRole(TaskInclude):
|
|||
|
||||
return ir
|
||||
|
||||
def copy(self, exclude_parent=False, exclude_tasks=False):
|
||||
|
||||
new_me = super(IncludeRole, self).copy(exclude_parent=exclude_parent, exclude_tasks=exclude_tasks)
|
||||
new_me.statically_loaded = self.statically_loaded
|
||||
def copy(self):
|
||||
new_me = super().copy()
|
||||
new_me._from_files = self._from_files.copy()
|
||||
new_me._parent_role = self._parent_role
|
||||
new_me._role_name = self._role_name
|
||||
new_me._role_path = self._role_path
|
||||
|
||||
return new_me
|
||||
|
||||
def get_include_params(self):
|
||||
|
|
|
|||
|
|
@ -485,23 +485,6 @@ class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatabl
|
|||
all_vars |= self.vars
|
||||
return all_vars
|
||||
|
||||
def copy(self, exclude_parent: bool = False, exclude_tasks: bool = False) -> Task:
|
||||
new_me = super(Task, self).copy()
|
||||
|
||||
new_me._parent = None
|
||||
if self._parent and not exclude_parent:
|
||||
new_me._parent = self._parent.copy(exclude_tasks=exclude_tasks)
|
||||
|
||||
new_me._role = None
|
||||
if self._role:
|
||||
new_me._role = self._role
|
||||
|
||||
new_me.implicit = self.implicit
|
||||
new_me._resolved_action = self._resolved_action
|
||||
new_me._uuid = self._uuid
|
||||
|
||||
return new_me
|
||||
|
||||
def set_loader(self, loader):
|
||||
"""
|
||||
Sets the loader on this object and recursively on parent, child objects.
|
||||
|
|
|
|||
|
|
@ -98,11 +98,6 @@ class TaskInclude(Task):
|
|||
|
||||
return ds
|
||||
|
||||
def copy(self, exclude_parent=False, exclude_tasks=False):
|
||||
new_me = super(TaskInclude, self).copy(exclude_parent=exclude_parent, exclude_tasks=exclude_tasks)
|
||||
new_me.statically_loaded = self.statically_loaded
|
||||
return new_me
|
||||
|
||||
def build_parent_block(self):
|
||||
"""
|
||||
This method is used to create the parent block for the included tasks
|
||||
|
|
|
|||
|
|
@ -451,8 +451,7 @@ class StrategyBase:
|
|||
|
||||
task = Task()
|
||||
else:
|
||||
task = found_task.copy(exclude_parent=True, exclude_tasks=True)
|
||||
task._parent = found_task._parent
|
||||
task = found_task.copy()
|
||||
|
||||
task.from_attrs(wire_task_result.task_fields)
|
||||
|
||||
|
|
@ -773,8 +772,7 @@ class StrategyBase:
|
|||
"""
|
||||
A proven safe and performant way to create a copy of an included file
|
||||
"""
|
||||
ti_copy = included_file._task.copy(exclude_parent=True)
|
||||
ti_copy._parent = included_file._task._parent
|
||||
ti_copy = included_file._task.copy()
|
||||
|
||||
temp_vars = ti_copy.vars | included_file._vars
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue