summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/ansible/executor/play_iterator.py62
-rw-r--r--lib/ansible/module_utils/eos.py35
-rw-r--r--lib/ansible/module_utils/iosxr.py35
-rw-r--r--lib/ansible/module_utils/netcmd.py35
-rw-r--r--lib/ansible/module_utils/network.py36
-rw-r--r--lib/ansible/module_utils/urls.py71
-rw-r--r--lib/ansible/module_utils/vyos.py35
m---------lib/ansible/modules/core14
m---------lib/ansible/modules/extras18
-rw-r--r--lib/ansible/playbook/base.py44
-rw-r--r--lib/ansible/playbook/block.py1
-rw-r--r--test/integration/Makefile4
-rw-r--r--test/units/executor/test_play_iterator.py2
13 files changed, 236 insertions, 156 deletions
diff --git a/lib/ansible/executor/play_iterator.py b/lib/ansible/executor/play_iterator.py
index d1366bbbc5..a8c00833e7 100644
--- a/lib/ansible/executor/play_iterator.py
+++ b/lib/ansible/executor/play_iterator.py
@@ -154,6 +154,8 @@ class PlayIterator:
self._play = play
self._blocks = []
+ self._task_uuid_cache = dict()
+
# Default options to gather
gather_subset = C.DEFAULT_GATHER_SUBSET
gather_timeout = C.DEFAULT_GATHER_TIMEOUT
@@ -179,12 +181,17 @@ class PlayIterator:
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)
if new_block.has_tasks():
+ self.cache_block_tasks(new_block)
self._blocks.append(new_block)
+ for handler_block in self._play.handlers:
+ self.cache_block_tasks(handler_block)
+
self._host_states = {}
start_at_matched = False
for host in inventory.get_hosts(self._play.hosts):
@@ -227,6 +234,18 @@ class PlayIterator:
return self._host_states[host.name].copy()
+ def cache_block_tasks(self, block):
+ def _cache_portion(p):
+ for t in p:
+ if isinstance(t, Block):
+ self.cache_block_tasks(t)
+ elif t._uuid not in self._task_uuid_cache:
+ self._task_uuid_cache[t._uuid] = t
+
+ for portion in (block.block, block.rescue, block.always):
+ if portion is not None:
+ _cache_portion(portion)
+
def get_next_task_for_host(self, host, peek=False):
display.debug("getting the next task for host %s" % host.name)
@@ -514,46 +533,7 @@ class PlayIterator:
else:
the_uuid = task
- def _search_block(block):
- '''
- helper method to check a block's task lists (block/rescue/always)
- for a given task uuid. If a Block is encountered in the place of a
- task, it will be recursively searched (this happens when a task
- include inserts one or more blocks into a task list).
- '''
- for b in (block.block, block.rescue, block.always):
- for t in b:
- if isinstance(t, Block):
- res = _search_block(t)
- if res:
- return res
- elif t._uuid == the_uuid:
- return t
- return None
-
- def _search_state(state):
- for block in state._blocks:
- res = _search_block(block)
- if res:
- return res
- for child_state in (state.tasks_child_state, state.rescue_child_state, state.always_child_state):
- if child_state is not None:
- res = _search_state(child_state)
- if res:
- return res
- return None
-
- s = self.get_host_state(host)
- res = _search_state(s)
- if res:
- return res
-
- for block in self._play.handlers:
- res = _search_block(block)
- if res:
- return res
-
- return None
+ return self._task_uuid_cache.get(the_uuid, 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
@@ -590,5 +570,7 @@ class PlayIterator:
return state
def add_tasks(self, host, task_list):
+ for b in task_list:
+ self.cache_block_tasks(b)
self._host_states[host.name] = self._insert_tasks_into_state(self.get_host_state(host), task_list)
diff --git a/lib/ansible/module_utils/eos.py b/lib/ansible/module_utils/eos.py
index 355453ce8d..8f596df50d 100644
--- a/lib/ansible/module_utils/eos.py
+++ b/lib/ansible/module_utils/eos.py
@@ -1,20 +1,29 @@
+# This code is part of Ansible, but is an independent component.
+# This particular file snippet, and this file snippet only, is BSD licensed.
+# Modules you write using this snippet, which is embedded dynamically by Ansible
+# still belong to the author of the module, and may assign their own license
+# to the complete work.
#
-# (c) 2015 Peter Sprygada, <psprygada@ansible.com>
+# Copyright (c) 2015 Peter Sprygada, <psprygada@ansible.com>
#
-# This file is part of Ansible
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
#
-# 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.
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# 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/>.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
import re
diff --git a/lib/ansible/module_utils/iosxr.py b/lib/ansible/module_utils/iosxr.py
index 33c5888447..357ef6b42d 100644
--- a/lib/ansible/module_utils/iosxr.py
+++ b/lib/ansible/module_utils/iosxr.py
@@ -1,20 +1,29 @@
+# This code is part of Ansible, but is an independent component.
+# This particular file snippet, and this file snippet only, is BSD licensed.
+# Modules you write using this snippet, which is embedded dynamically by Ansible
+# still belong to the author of the module, and may assign their own license
+# to the complete work.
#
-# (c) 2015 Peter Sprygada, <psprygada@ansible.com>
+# Copyright (c) 2015 Peter Sprygada, <psprygada@ansible.com>
#
-# This file is part of Ansible
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
#
-# 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.
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# 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/>.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
import re
diff --git a/lib/ansible/module_utils/netcmd.py b/lib/ansible/module_utils/netcmd.py
index 651c437dd0..fc77af8d12 100644
--- a/lib/ansible/module_utils/netcmd.py
+++ b/lib/ansible/module_utils/netcmd.py
@@ -1,20 +1,29 @@
+# This code is part of Ansible, but is an independent component.
+# This particular file snippet, and this file snippet only, is BSD licensed.
+# Modules you write using this snippet, which is embedded dynamically by Ansible
+# still belong to the author of the module, and may assign their own license
+# to the complete work.
#
-# (c) 2015 Peter Sprygada, <psprygada@ansible.com>
+# Copyright (c) 2015 Peter Sprygada, <psprygada@ansible.com>
#
-# This file is part of Ansible
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
#
-# 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.
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# 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/>.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
import re
diff --git a/lib/ansible/module_utils/network.py b/lib/ansible/module_utils/network.py
index c16c6895df..27acbb7f8e 100644
--- a/lib/ansible/module_utils/network.py
+++ b/lib/ansible/module_utils/network.py
@@ -1,21 +1,31 @@
+# This code is part of Ansible, but is an independent component.
+# This particular file snippet, and this file snippet only, is BSD licensed.
+# Modules you write using this snippet, which is embedded dynamically by Ansible
+# still belong to the author of the module, and may assign their own license
+# to the complete work.
#
-# (c) 2015 Peter Sprygada, <psprygada@ansible.com>
+# Copyright (c) 2015 Peter Sprygada, <psprygada@ansible.com>
#
-# This file is part of Ansible
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
#
-# 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.
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# 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/>.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
+
import itertools
from ansible.module_utils.basic import AnsibleModule
diff --git a/lib/ansible/module_utils/urls.py b/lib/ansible/module_utils/urls.py
index bbbf0d8544..2ed8e10f15 100644
--- a/lib/ansible/module_utils/urls.py
+++ b/lib/ansible/module_utils/urls.py
@@ -81,6 +81,21 @@
# agrees to be bound by the terms and conditions of this License
# Agreement.
+'''
+The **urls** utils module offers a replacement for the urllib2 python library.
+
+urllib2 is the python stdlib way to retrieve files from the Internet but it
+lacks some security features (around verifying SSL certificates) that users
+should care about in most situations. Using the functions in this module corrects
+deficiencies in the urllib2 module wherever possible.
+
+There are also third-party libraries (for instance, requests) which can be used
+to replace urllib2 with a more secure library. However, all third party libraries
+require that the library be installed on the managed machine. That is an extra step
+for users making use of a module. If possible, avoid third party libraries by using
+this code instead.
+'''
+
import netrc
import os
import re
@@ -728,11 +743,11 @@ def maybe_add_ssl_handler(url, validate_certs):
def open_url(url, data=None, headers=None, method=None, use_proxy=True,
- force=False, last_mod_time=None, timeout=10, validate_certs=True,
- url_username=None, url_password=None, http_agent=None,
- force_basic_auth=False, follow_redirects='urllib2'):
+ force=False, last_mod_time=None, timeout=10, validate_certs=True,
+ url_username=None, url_password=None, http_agent=None,
+ force_basic_auth=False, follow_redirects='urllib2'):
'''
- Fetches a file from an HTTP/FTP server using urllib2
+ Sends a request via HTTP(S) or FTP using urllib2 (Python2) or urllib (Python3)
Does not require the module environment
'''
@@ -870,23 +885,49 @@ def url_argument_spec():
that will be requesting content via urllib/urllib2
'''
return dict(
- url = dict(),
- force = dict(default='no', aliases=['thirsty'], type='bool'),
- http_agent = dict(default='ansible-httpget'),
- use_proxy = dict(default='yes', type='bool'),
- validate_certs = dict(default='yes', type='bool'),
- url_username = dict(required=False),
- url_password = dict(required=False),
- force_basic_auth = dict(required=False, type='bool', default='no'),
+ url=dict(),
+ force=dict(default='no', aliases=['thirsty'], type='bool'),
+ http_agent=dict(default='ansible-httpget'),
+ use_proxy=dict(default='yes', type='bool'),
+ validate_certs=dict(default='yes', type='bool'),
+ url_username=dict(required=False),
+ url_password=dict(required=False),
+ force_basic_auth=dict(required=False, type='bool', default='no'),
)
def fetch_url(module, url, data=None, headers=None, method=None,
use_proxy=True, force=False, last_mod_time=None, timeout=10):
- '''
- Fetches a file from an HTTP/FTP server using urllib2. Requires the module environment
- '''
+ '''Sends a request via HTTP(S) or FTP (needs the module as parameter)
+
+ :arg module: The AnsibleModule (used to get username, password etc. (s.b.).
+ :arg url: The url to use.
+
+ :kwarg data: The data to be sent (in case of POST/PUT).
+ :kwarg headers: A dict with the request headers.
+ :kwarg method: "POST", "PUT", etc.
+ :kwarg boolean use_proxy: Default: True
+ :kwarg boolean force: If True: Do not get a cached copy (Default: False)
+ :kwarg last_mod_time: Default: None
+ :kwarg int timeout: Default: 10
+
+ :returns: A tuple of (**response**, **info**). Use ``response.body()`` to read the data.
+ The **info** contains the 'status' and other meta data. When a HttpError (status > 400)
+ occurred then ``info['body']`` contains the error response data::
+
+ Example::
+
+ data={...}
+ resp, info = fetch_url("http://example.com",
+ data=module.jsonify(data)
+ header={Content-type': 'application/json'},
+ method="POST")
+ status_code = info["status"]
+ body = resp.read()
+ if status_code >= 400 :
+ body = info['body']
+'''
if not HAS_URLPARSE:
module.fail_json(msg='urlparse is not installed')
diff --git a/lib/ansible/module_utils/vyos.py b/lib/ansible/module_utils/vyos.py
index 7002e46d74..840d3b0b2e 100644
--- a/lib/ansible/module_utils/vyos.py
+++ b/lib/ansible/module_utils/vyos.py
@@ -1,20 +1,29 @@
+# This code is part of Ansible, but is an independent component.
+# This particular file snippet, and this file snippet only, is BSD licensed.
+# Modules you write using this snippet, which is embedded dynamically by Ansible
+# still belong to the author of the module, and may assign their own license
+# to the complete work.
#
-# (c) 2015 Peter Sprygada, <psprygada@ansible.com>
+# Copyright (c) 2015 Peter Sprygada, <psprygada@ansible.com>
#
-# This file is part of Ansible
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
#
-# 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.
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
#
-# 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/>.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
import itertools
diff --git a/lib/ansible/modules/core b/lib/ansible/modules/core
-Subproject 5d7b46e0ddb7f55f1dbc6f69b973f1bcd385102
+Subproject a216ef210b3d8fc332afb252cdf787f4744585f
diff --git a/lib/ansible/modules/extras b/lib/ansible/modules/extras
-Subproject eaa71f51d652d803bff527a5941c027cb206d88
+Subproject 39153ea1548d208545a31d4fa581c70c22e90c9
diff --git a/lib/ansible/playbook/base.py b/lib/ansible/playbook/base.py
index ca6ad67c17..b9edce54e4 100644
--- a/lib/ansible/playbook/base.py
+++ b/lib/ansible/playbook/base.py
@@ -65,9 +65,6 @@ class Base:
_ignore_errors = FieldAttribute(isa='bool')
_check_mode = FieldAttribute(isa='bool')
- # other internal params
- _finalized = False
-
# param names which have been deprecated/removed
DEPRECATED_ATTRIBUTES = [
'sudo', 'sudo_user', 'sudo_pass', 'sudo_exe', 'sudo_flags',
@@ -81,9 +78,12 @@ class Base:
self._loader = None
self._variable_manager = None
+ # other internal params
+ self._validated = False
+ self._finalized = False
+
# every object gets a random uuid:
self._uuid = uuid.uuid4()
- #self._uuid = 1
# and initialize the base attributes
self._initialize_base_attributes()
@@ -272,20 +272,25 @@ class Base:
def validate(self, all_vars=dict()):
''' validation that is done at parse time, not load time '''
- # walk all fields in the object
- for (name, attribute) in iteritems(self._get_base_attributes()):
+ if not self._validated:
+ # walk all fields in the object
+ for (name, attribute) in iteritems(self._get_base_attributes()):
- # run validator only if present
- method = getattr(self, '_validate_%s' % name, None)
- if method:
- method(attribute, name, getattr(self, name))
- else:
- # and make sure the attribute is of the type it should be
- value = getattr(self, name)
- if value is not None:
- if attribute.isa == 'string' and isinstance(value, (list, dict)):
- raise AnsibleParserError("The field '%s' is supposed to be a string type,"
- " however the incoming data structure is a %s" % (name, type(value)), obj=self.get_ds())
+ # run validator only if present
+ method = getattr(self, '_validate_%s' % name, None)
+ if method:
+ method(attribute, name, getattr(self, name))
+ else:
+ # and make sure the attribute is of the type it should be
+ value = getattr(self, name)
+ if value is not None:
+ if attribute.isa == 'string' and isinstance(value, (list, dict)):
+ raise AnsibleParserError(
+ "The field '%s' is supposed to be a string type,"
+ " however the incoming data structure is a %s" % (name, type(value)), obj=self.get_ds()
+ )
+
+ self._validated = True
def copy(self):
'''
@@ -303,9 +308,10 @@ class Base:
else:
setattr(new_me, name, attr_val)
- new_me._loader = self._loader
+ new_me._loader = self._loader
new_me._variable_manager = self._variable_manager
-
+ 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
diff --git a/lib/ansible/playbook/block.py b/lib/ansible/playbook/block.py
index 493cec2b22..ec6298a2c1 100644
--- a/lib/ansible/playbook/block.py
+++ b/lib/ansible/playbook/block.py
@@ -192,6 +192,7 @@ class Block(Base, Become, Conditional, Taggable):
if self._role:
new_me._role = self._role
+ new_me.validate()
return new_me
def serialize(self):
diff --git a/test/integration/Makefile b/test/integration/Makefile
index cc5d26a02e..bf861f641d 100644
--- a/test/integration/Makefile
+++ b/test/integration/Makefile
@@ -202,13 +202,13 @@ blocks: setup
# remove old output log
rm -f block_test.out
# run the test and check to make sure the right number of completions was logged
- ansible-playbook -vv -e outputdir=$(TEST_DIR) test_blocks/main.yml | tee block_test.out
+ ansible-playbook -vv $(TEST_FLAGS) -e outputdir=$(TEST_DIR) test_blocks/main.yml | tee block_test.out
env python -c 'import sys, re; sys.stdout.write(re.sub("\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]", "", sys.stdin.read()))' <block_test.out >block_test_wo_colors.out
[ "$$(grep 'TEST COMPLETE' block_test.out | wc -l | sed 's/ *//')" = "$$(egrep '^[0-9]+ plays in' block_test_wo_colors.out | cut -f1 -d' ')" ]
# cleanup the output log again, to make sure the test is clean
rm -f block_test.out block_test_wo_colors.out
# run test with free strategy and again count the completions
- ansible-playbook -vv -e outputdir=$(TEST_DIR) test_blocks/main.yml -e test_strategy=free | tee block_test.out
+ ansible-playbook -vv $(TEST_FLAGS) -e outputdir=$(TEST_DIR) test_blocks/main.yml -e test_strategy=free | tee block_test.out
env python -c 'import sys, re; sys.stdout.write(re.sub("\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]", "", sys.stdin.read()))' <block_test.out >block_test_wo_colors.out
[ "$$(grep 'TEST COMPLETE' block_test.out | wc -l | sed 's/ *//')" = "$$(egrep '^[0-9]+ plays in' block_test_wo_colors.out | cut -f1 -d' ')" ]
diff --git a/test/units/executor/test_play_iterator.py b/test/units/executor/test_play_iterator.py
index c0f23e531d..671deaf279 100644
--- a/test/units/executor/test_play_iterator.py
+++ b/test/units/executor/test_play_iterator.py
@@ -329,7 +329,7 @@ class TestPlayIterator(unittest.TestCase):
# test the high-level add_tasks() method
s = HostState(blocks=[0,1,2])
itr._insert_tasks_into_state = MagicMock(return_value=s)
- itr.add_tasks(hosts[0], [3,4,5])
+ itr.add_tasks(hosts[0], [MagicMock(), MagicMock(), MagicMock()])
self.assertEqual(itr._host_states[hosts[0].name], s)
# now actually test the lower-level method that does the work