Merge "Fix status_url section in zuul.conf-sample" into feature/zuulv3
diff --git a/doc/source/admin/drivers/timer.rst b/doc/source/admin/drivers/timer.rst
index c70df5c..c8afdd7 100644
--- a/doc/source/admin/drivers/timer.rst
+++ b/doc/source/admin/drivers/timer.rst
@@ -21,4 +21,4 @@
**time**
The time specification in cron syntax. Only the 5 part syntax is
supported, not the symbolic names. Example: ``0 0 * * *`` runs at
- midnight.
+ midnight. The first weekday is Monday.
diff --git a/zuul/ansible/callback/zuul_stream.py b/zuul/ansible/callback/zuul_stream.py
index 5c2ce8a..c4dd532 100644
--- a/zuul/ansible/callback/zuul_stream.py
+++ b/zuul/ansible/callback/zuul_stream.py
@@ -13,8 +13,15 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
+# This is not needed in python3 - but it is needed in python2 because there
+# is a json module in ansible.plugins.callback and python2 gets confused.
+# Easy local testing with ansible-playbook is handy when hacking on zuul_stream
+# so just put in the __future__ statement.
+from __future__ import absolute_import
+
import datetime
import logging
+import json
import os
import socket
import threading
@@ -143,16 +150,33 @@
def v2_playbook_on_start(self, playbook):
self._playbook_name = os.path.splitext(playbook._file_name)[0]
+ def v2_playbook_on_include(self, included_file):
+ for host in included_file._hosts:
+ self._log("{host} | included: {filename}".format(
+ host=host.name,
+ filename=included_file._filename))
+
def v2_playbook_on_play_start(self, play):
self._play = play
+ # Get the hostvars from just one host - the vars we're looking for will
+ # be identical on all of them
+ hostvars = self._play._variable_manager._hostvars
+ a_host = next(iter(hostvars.keys()))
+ self.phase = hostvars[a_host]['zuul_execution_phase']
+ if self.phase != 'run':
+ self.phase = '{phase}-{index}'.format(
+ phase=self.phase,
+ index=hostvars[a_host]['zuul_execution_phase_index'])
+
+ # the name of a play defaults to the hosts string
name = play.get_name().strip()
- if not name:
- msg = u"PLAY"
- else:
- msg = u"PLAY [{playbook} : {name}]".format(
- playbook=self._playbook_name, name=name)
+ msg = u"PLAY [{phase} : {playbook} : {name}]".format(
+ phase=self.phase,
+ playbook=self._playbook_name, name=name)
self._log(msg)
+ # Log an extra blank line to get space after each play
+ self._log("")
def v2_playbook_on_task_start(self, task, is_conditional):
self._task = task
@@ -234,12 +258,11 @@
pass
else:
self._log_message(
- result=result,
- msg="Results: => {results}".format(
- results=self._dump_results(result_dict)),
- status='ERROR')
+ result=result, status='ERROR', result_dict=result_dict)
if ignore_errors:
self._log_message(result, "Ignoring Errors", status="ERROR")
+ # Log an extra blank line to get space after each task
+ self._log("")
def v2_runner_on_ok(self, result):
if (self._play.strategy == 'free'
@@ -285,9 +308,8 @@
elif result._task.action not in ('command', 'shell'):
self._log_message(
result=result,
- msg="Results: => {results}".format(
- results=self._dump_results(result_dict)),
- status=status)
+ status=status,
+ result_dict=result_dict)
elif 'results' in result_dict:
for res in result_dict['results']:
self._log_message(
@@ -300,6 +322,8 @@
result,
"Runtime: {delta} Start: {start} End: {end}".format(
**result_dict))
+ # Log an extra blank line to get space after each task
+ self._log("")
def v2_runner_item_on_ok(self, result):
result_dict = dict(result._result)
@@ -313,9 +337,8 @@
if result._task.action not in ('command', 'shell'):
self._log_message(
result=result,
- msg="Item: {item} => {results}".format(
- item=result_dict['item'],
- results=self._dump_results(result_dict)),
+ msg="Item: {item}".format(item=result_dict['item']),
+ result_dict=result_dict,
status=status)
else:
self._log_message(
@@ -325,6 +348,8 @@
if self._deferred_result:
self._process_deferred(result)
+ # Log an extra blank line to get space after each task
+ self._log("")
def v2_runner_item_on_failed(self, result):
result_dict = dict(result._result)
@@ -333,10 +358,9 @@
if result._task.action not in ('command', 'shell'):
self._log_message(
result=result,
- msg="Item: {item} => {results}".format(
- item=result_dict['item'],
- results=self._dump_results(result_dict)),
- status='ERROR')
+ msg="Item: {item}".format(item=result_dict['item']),
+ status='ERROR',
+ result_dict=result_dict)
else:
self._log_message(
result,
@@ -345,6 +369,24 @@
if self._deferred_result:
self._process_deferred(result)
+ # Log an extra blank line to get space after each task
+ self._log("")
+
+ def v2_playbook_on_stats(self, stats):
+
+ # Log an extra blank line to get space before the stats
+ self._log("")
+ self._log("PLAY RECAP")
+
+ hosts = sorted(stats.processed.keys())
+ for host in hosts:
+ t = stats.summarize(host)
+ self._log(
+ "{host} |"
+ " ok: {ok}"
+ " changed: {changed}"
+ " unreachable: {unreachable}"
+ " failed: {failures}".format(host=host, **t))
def _process_deferred(self, result):
self._items_done = True
@@ -383,6 +425,8 @@
task=task_name,
args=args)
self._log(msg)
+ # Log an extra blank line to get space after each task
+ self._log("")
return task
def _get_task_hosts(self, task):
@@ -401,10 +445,26 @@
hosts = play_vars.keys()
return hosts
- def _log_message(self, result, msg, status="ok"):
+ def _dump_result_dict(self, result_dict):
+ result_dict = result_dict.copy()
+ for key in list(result_dict.keys()):
+ if key.startswith('_ansible') or key == 'zuul_log_id':
+ del result_dict[key]
+ return result_dict
+
+ def _log_message(self, result, msg=None, status="ok", result_dict=None):
hostname = self._get_hostname(result)
- self._log("{host} | {status}: {msg}".format(
- host=hostname, status=status, msg=msg))
+ if msg:
+ self._log("{host} | {status}: {msg}".format(
+ host=hostname, status=status, msg=msg))
+ else:
+ self._log("{host} | {status}".format(
+ host=hostname, status=status, msg=msg))
+ if result_dict:
+ result_string = json.dumps(self._dump_result_dict(result_dict),
+ indent=2, sort_keys=True)
+ for line in result_string.split('\n'):
+ self._log("{host} | {line}".format(host=hostname, line=line))
def _get_hostname(self, result):
delegated_vars = result._result.get('_ansible_delegated_vars', None)
diff --git a/zuul/executor/server.py b/zuul/executor/server.py
index 818c7e2..cdd082e 100644
--- a/zuul/executor/server.py
+++ b/zuul/executor/server.py
@@ -896,10 +896,10 @@
result = None
pre_failed = False
- for count, playbook in enumerate(self.jobdir.pre_playbooks):
+ for index, playbook in enumerate(self.jobdir.pre_playbooks):
# TODOv3(pabelanger): Implement pre-run timeout setting.
pre_status, pre_code = self.runAnsiblePlaybook(
- playbook, args['timeout'], phase='pre', count=count)
+ playbook, args['timeout'], phase='pre', index=index)
if pre_status != self.RESULT_NORMAL or pre_code != 0:
# These should really never fail, so return None and have
# zuul try again
@@ -925,10 +925,10 @@
else:
result = 'FAILURE'
- for count, playbook in enumerate(self.jobdir.post_playbooks):
+ for index, playbook in enumerate(self.jobdir.post_playbooks):
# TODOv3(pabelanger): Implement post-run timeout setting.
post_status, post_code = self.runAnsiblePlaybook(
- playbook, args['timeout'], success, phase='post', count=count)
+ playbook, args['timeout'], success, phase='post', index=index)
if post_status != self.RESULT_NORMAL or post_code != 0:
# If we encountered a pre-failure, that takes
# precedence over the post result.
@@ -1388,7 +1388,7 @@
return (self.RESULT_NORMAL, ret)
def runAnsiblePlaybook(self, playbook, timeout, success=None,
- phase=None, count=None):
+ phase=None, index=None):
env_copy = os.environ.copy()
env_copy['LOGNAME'] = 'zuul'
@@ -1405,8 +1405,8 @@
if phase:
cmd.extend(['-e', 'zuul_execution_phase=%s' % phase])
- if count is not None:
- cmd.extend(['-e', 'zuul_execution_phase_count=%s' % count])
+ if index is not None:
+ cmd.extend(['-e', 'zuul_execution_phase_index=%s' % index])
result, code = self.runAnsible(
cmd=cmd, timeout=timeout, trusted=playbook.trusted)