mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
feat: implement timestamp callback plugin to show simple timestamp for each header (#8308)
* feat: add community.general.timestamp callback plugin * feat: add minimal integration tests for timestamp callback plugin * feat: add maintainers for timestamp callback plugin * fix: correct license * fix: remove type annotation for the older python environment * fix: remove unnecessary comment Co-authored-by: Felix Fontein <felix@fontein.de> * fix: add trailing period Co-authored-by: Felix Fontein <felix@fontein.de> * fix: split long description into list Co-authored-by: Felix Fontein <felix@fontein.de> * fix: remove default and add type Co-authored-by: Felix Fontein <felix@fontein.de> * fix; add type Co-authored-by: Felix Fontein <felix@fontein.de> * fix: split long description into list Co-authored-by: Felix Fontein <felix@fontein.de> * fix: improve description for format_string to describe usable format codes * fix: clarify the original codes and add copyright from that * fix: shorten long lines * fix: correct link format * fix: add seealso section * fix: add ignore entries for EOL CI * fix: update seealso to correctly associate with related plugin Co-authored-by: Felix Fontein <felix@fontein.de> --------- Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
parent
cd01a928ab
commit
bb73f28bf5
6 changed files with 203 additions and 0 deletions
2
.github/BOTMETA.yml
vendored
2
.github/BOTMETA.yml
vendored
|
@ -91,6 +91,8 @@ files:
|
||||||
maintainers: ryancurrah
|
maintainers: ryancurrah
|
||||||
$callbacks/syslog_json.py:
|
$callbacks/syslog_json.py:
|
||||||
maintainers: imjoseangel
|
maintainers: imjoseangel
|
||||||
|
$callbacks/timestamp.py:
|
||||||
|
maintainers: kurokobo
|
||||||
$callbacks/unixy.py:
|
$callbacks/unixy.py:
|
||||||
labels: unixy
|
labels: unixy
|
||||||
maintainers: akatch
|
maintainers: akatch
|
||||||
|
|
127
plugins/callback/timestamp.py
Normal file
127
plugins/callback/timestamp.py
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright (c) 2024, kurokobo <kurokobo@protonmail.com>
|
||||||
|
# Copyright (c) 2014, Michael DeHaan <michael.dehaan@gmail.com>
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
DOCUMENTATION = r"""
|
||||||
|
name: timestamp
|
||||||
|
type: stdout
|
||||||
|
short_description: Adds simple timestamp for each header
|
||||||
|
version_added: 9.0.0
|
||||||
|
description:
|
||||||
|
- This callback adds simple timestamp for each header.
|
||||||
|
author: kurokobo (@kurokobo)
|
||||||
|
options:
|
||||||
|
timezone:
|
||||||
|
description:
|
||||||
|
- Timezone to use for the timestamp in IANA time zone format.
|
||||||
|
- For example C(America/New_York), C(Asia/Tokyo)). Ignored on Python < 3.9.
|
||||||
|
ini:
|
||||||
|
- section: callback_timestamp
|
||||||
|
key: timezone
|
||||||
|
env:
|
||||||
|
- name: ANSIBLE_CALLBACK_TIMESTAMP_TIMEZONE
|
||||||
|
type: string
|
||||||
|
format_string:
|
||||||
|
description:
|
||||||
|
- Format of the timestamp shown to user in 1989 C standard format.
|
||||||
|
- >
|
||||||
|
Refer to L(the Python documentation,https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes)
|
||||||
|
for the available format codes.
|
||||||
|
ini:
|
||||||
|
- section: callback_timestamp
|
||||||
|
key: format_string
|
||||||
|
env:
|
||||||
|
- name: ANSIBLE_CALLBACK_TIMESTAMP_FORMAT_STRING
|
||||||
|
default: "%H:%M:%S"
|
||||||
|
type: string
|
||||||
|
seealso:
|
||||||
|
- plugin: ansible.posix.profile_tasks
|
||||||
|
plugin_type: callback
|
||||||
|
description: >
|
||||||
|
You can use P(ansible.posix.profile_tasks#callback) callback plugin to time individual tasks and overall execution time
|
||||||
|
with detailed timestamps.
|
||||||
|
extends_documentation_fragment:
|
||||||
|
- ansible.builtin.default_callback
|
||||||
|
- ansible.builtin.result_format_callback
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from ansible.plugins.callback.default import CallbackModule as Default
|
||||||
|
from ansible.utils.display import get_text_width
|
||||||
|
from ansible.module_utils.common.text.converters import to_text
|
||||||
|
from datetime import datetime
|
||||||
|
import types
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Store whether the zoneinfo module is available
|
||||||
|
_ZONEINFO_AVAILABLE = sys.version_info >= (3, 9)
|
||||||
|
|
||||||
|
|
||||||
|
def get_datetime_now(tz):
|
||||||
|
"""
|
||||||
|
Returns the current timestamp with the specified timezone
|
||||||
|
"""
|
||||||
|
return datetime.now(tz=tz)
|
||||||
|
|
||||||
|
|
||||||
|
def banner(self, msg, color=None, cows=True):
|
||||||
|
"""
|
||||||
|
Prints a header-looking line with cowsay or stars with length depending on terminal width (3 minimum) with trailing timestamp
|
||||||
|
|
||||||
|
Based on the banner method of Display class from ansible.utils.display
|
||||||
|
|
||||||
|
https://github.com/ansible/ansible/blob/4403519afe89138042108e237aef317fd5f09c33/lib/ansible/utils/display.py#L511
|
||||||
|
"""
|
||||||
|
timestamp = get_datetime_now(self.timestamp_tzinfo).strftime(self.timestamp_format_string)
|
||||||
|
timestamp_len = get_text_width(timestamp) + 1 # +1 for leading space
|
||||||
|
|
||||||
|
msg = to_text(msg)
|
||||||
|
if self.b_cowsay and cows:
|
||||||
|
try:
|
||||||
|
self.banner_cowsay("%s @ %s" % (msg, timestamp))
|
||||||
|
return
|
||||||
|
except OSError:
|
||||||
|
self.warning("somebody cleverly deleted cowsay or something during the PB run. heh.")
|
||||||
|
|
||||||
|
msg = msg.strip()
|
||||||
|
try:
|
||||||
|
star_len = self.columns - get_text_width(msg) - timestamp_len
|
||||||
|
except EnvironmentError:
|
||||||
|
star_len = self.columns - len(msg) - timestamp_len
|
||||||
|
if star_len <= 3:
|
||||||
|
star_len = 3
|
||||||
|
stars = "*" * star_len
|
||||||
|
self.display("\n%s %s %s" % (msg, stars, timestamp), color=color)
|
||||||
|
|
||||||
|
|
||||||
|
class CallbackModule(Default):
|
||||||
|
CALLBACK_VERSION = 2.0
|
||||||
|
CALLBACK_TYPE = "stdout"
|
||||||
|
CALLBACK_NAME = "community.general.timestamp"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(CallbackModule, self).__init__()
|
||||||
|
|
||||||
|
# Replace the banner method of the display object with the custom one
|
||||||
|
self._display.banner = types.MethodType(banner, self._display)
|
||||||
|
|
||||||
|
def set_options(self, task_keys=None, var_options=None, direct=None):
|
||||||
|
super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)
|
||||||
|
|
||||||
|
# Store zoneinfo for specified timezone if available
|
||||||
|
tzinfo = None
|
||||||
|
if _ZONEINFO_AVAILABLE and self.get_option("timezone"):
|
||||||
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
|
tzinfo = ZoneInfo(self.get_option("timezone"))
|
||||||
|
|
||||||
|
# Inject options into the display object
|
||||||
|
setattr(self._display, "timestamp_tzinfo", tzinfo)
|
||||||
|
setattr(self._display, "timestamp_format_string", self.get_option("format_string"))
|
6
tests/integration/targets/callback_timestamp/aliases
Normal file
6
tests/integration/targets/callback_timestamp/aliases
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# Copyright (c) 2024, kurokobo <kurokobo@protonmail.com>
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or <https://www.gnu.org/licenses/gpl-3.0.txt>)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
azp/posix/1
|
||||||
|
needs/target/callback
|
66
tests/integration/targets/callback_timestamp/tasks/main.yml
Normal file
66
tests/integration/targets/callback_timestamp/tasks/main.yml
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
---
|
||||||
|
####################################################################
|
||||||
|
# WARNING: These are designed specifically for Ansible tests #
|
||||||
|
# and should not be used as examples of how to write Ansible roles #
|
||||||
|
####################################################################
|
||||||
|
|
||||||
|
# Copyright (c) 2024, kurokobo <kurokobo@protonmail.com>
|
||||||
|
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
include_role:
|
||||||
|
name: callback
|
||||||
|
vars:
|
||||||
|
tests:
|
||||||
|
- name: Enable timestamp in the default length
|
||||||
|
environment:
|
||||||
|
ANSIBLE_NOCOLOR: 'true'
|
||||||
|
ANSIBLE_FORCE_COLOR: 'false'
|
||||||
|
ANSIBLE_STDOUT_CALLBACK: community.general.timestamp
|
||||||
|
ANSIBLE_CALLBACK_TIMESTAMP_FORMAT_STRING: "15:04:05"
|
||||||
|
playbook: |
|
||||||
|
- hosts: testhost
|
||||||
|
gather_facts: false
|
||||||
|
tasks:
|
||||||
|
- name: Sample task name
|
||||||
|
debug:
|
||||||
|
msg: sample debug msg
|
||||||
|
expected_output: [
|
||||||
|
"",
|
||||||
|
"PLAY [testhost] ******************************************************* 15:04:05",
|
||||||
|
"",
|
||||||
|
"TASK [Sample task name] *********************************************** 15:04:05",
|
||||||
|
"ok: [testhost] => {",
|
||||||
|
" \"msg\": \"sample debug msg\"",
|
||||||
|
"}",
|
||||||
|
"",
|
||||||
|
"PLAY RECAP ************************************************************ 15:04:05",
|
||||||
|
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
|
||||||
|
]
|
||||||
|
|
||||||
|
- name: Enable timestamp in the longer length
|
||||||
|
environment:
|
||||||
|
ANSIBLE_NOCOLOR: 'true'
|
||||||
|
ANSIBLE_FORCE_COLOR: 'false'
|
||||||
|
ANSIBLE_STDOUT_CALLBACK: community.general.timestamp
|
||||||
|
ANSIBLE_CALLBACK_TIMESTAMP_FORMAT_STRING: "2006-01-02T15:04:05"
|
||||||
|
playbook: |
|
||||||
|
- hosts: testhost
|
||||||
|
gather_facts: false
|
||||||
|
tasks:
|
||||||
|
- name: Sample task name
|
||||||
|
debug:
|
||||||
|
msg: sample debug msg
|
||||||
|
expected_output: [
|
||||||
|
"",
|
||||||
|
"PLAY [testhost] ******************************************** 2006-01-02T15:04:05",
|
||||||
|
"",
|
||||||
|
"TASK [Sample task name] ************************************ 2006-01-02T15:04:05",
|
||||||
|
"ok: [testhost] => {",
|
||||||
|
" \"msg\": \"sample debug msg\"",
|
||||||
|
"}",
|
||||||
|
"",
|
||||||
|
"PLAY RECAP ************************************************* 2006-01-02T15:04:05",
|
||||||
|
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
|
||||||
|
]
|
|
@ -1,4 +1,5 @@
|
||||||
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
|
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
|
||||||
|
plugins/callback/timestamp.py validate-modules:invalid-documentation
|
||||||
plugins/lookup/etcd.py validate-modules:invalid-documentation
|
plugins/lookup/etcd.py validate-modules:invalid-documentation
|
||||||
plugins/lookup/etcd3.py validate-modules:invalid-documentation
|
plugins/lookup/etcd3.py validate-modules:invalid-documentation
|
||||||
plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice
|
plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
|
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
|
||||||
|
plugins/callback/timestamp.py validate-modules:invalid-documentation
|
||||||
plugins/lookup/etcd.py validate-modules:invalid-documentation
|
plugins/lookup/etcd.py validate-modules:invalid-documentation
|
||||||
plugins/lookup/etcd3.py validate-modules:invalid-documentation
|
plugins/lookup/etcd3.py validate-modules:invalid-documentation
|
||||||
plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice
|
plugins/modules/consul_session.py validate-modules:parameter-state-invalid-choice
|
||||||
|
|
Loading…
Reference in a new issue