Bump version of main to 12.0.0; execute announced deprecations (#10883)

* Bump version to 12.0.0.

* Remove deprecated modules and plugins.

* state is now required.

* Change default of prepend_hash from auto to never.

* Remove support for force=''.

* Always delegate 'debug'.

* Remove ignore_value_none and ctx_ignore_none parameters.

* Remove parameters on_success and on_failure.

* Update BOTMETA.

* Adjust docs reference.

* Forgot required=True.

* Fix changelog fragment.

* Adjust unit tests.

* Fix changelog.

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

---------

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
This commit is contained in:
Felix Fontein 2025-10-09 13:50:07 +02:00 committed by GitHub
commit 0b72737cab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 43 additions and 901 deletions

10
.github/BOTMETA.yml vendored
View file

@ -99,7 +99,6 @@ files:
$callbacks/unixy.py: $callbacks/unixy.py:
labels: unixy labels: unixy
maintainers: akatch maintainers: akatch
$callbacks/yaml.py: {}
$connections/: $connections/:
labels: connections labels: connections
$connections/chroot.py: {} $connections/chroot.py: {}
@ -394,9 +393,6 @@ files:
$module_utils/puppet.py: $module_utils/puppet.py:
labels: puppet labels: puppet
maintainers: russoz maintainers: russoz
$module_utils/pure.py:
labels: pure pure_storage
maintainers: $team_purestorage
$module_utils/redfish_utils.py: $module_utils/redfish_utils.py:
labels: redfish_utils labels: redfish_utils
maintainers: $team_redfish maintainers: $team_redfish
@ -480,8 +476,6 @@ files:
keywords: beadm dladm illumos ipadm nexenta omnios openindiana pfexec smartos solaris sunos zfs zpool keywords: beadm dladm illumos ipadm nexenta omnios openindiana pfexec smartos solaris sunos zfs zpool
labels: beadm solaris labels: beadm solaris
maintainers: $team_solaris maintainers: $team_solaris
$modules/bearychat.py:
maintainers: tonyseek
$modules/bigpanda.py: $modules/bigpanda.py:
ignore: hkariti ignore: hkariti
$modules/bitbucket_: $modules/bitbucket_:
@ -587,9 +581,6 @@ files:
$modules/etcd3.py: $modules/etcd3.py:
ignore: vfauth ignore: vfauth
maintainers: evrardjp maintainers: evrardjp
$modules/facter.py:
labels: facter
maintainers: $team_ansible_core gamethis
$modules/facter_facts.py: $modules/facter_facts.py:
labels: facter labels: facter
maintainers: russoz $team_ansible_core gamethis maintainers: russoz $team_ansible_core gamethis
@ -1610,7 +1601,6 @@ macros:
team_networking: NilashishC Qalthos danielmellado ganeshrn justjais trishnaguha sganesh-infoblox privateip team_networking: NilashishC Qalthos danielmellado ganeshrn justjais trishnaguha sganesh-infoblox privateip
team_opennebula: ilicmilan meerkampdvv rsmontero xorel nilsding team_opennebula: ilicmilan meerkampdvv rsmontero xorel nilsding
team_oracle: manojmeda mross22 nalsaber team_oracle: manojmeda mross22 nalsaber
team_purestorage: bannaych dnix101 genegr lionmax opslounge raekins sdodsley sile16
team_redfish: mraineri tomasg2012 xmadsen renxulei rajeevkallur bhavya06 jyundt team_redfish: mraineri tomasg2012 xmadsen renxulei rajeevkallur bhavya06 jyundt
team_rhsm: cnsnyder ptoscano team_rhsm: cnsnyder ptoscano
team_scaleway: remyleone abarbare team_scaleway: remyleone abarbare

View file

@ -0,0 +1,16 @@
removed_features:
- "yaml callback plugin - the deprecated plugin has been removed. Use the default callback with ``result_format=yaml`` instead (https://github.com/ansible-collections/community.general/pull/10883)."
- "purestorage doc fragment - the modules using this doc fragment have been removed from community.general 3.0.0 (https://github.com/ansible-collections/community.general/pull/10883)."
- "pure module utils - the modules using this module utils have been removed from community.general 3.0.0 (https://github.com/ansible-collections/community.general/pull/10883)."
- "bearychat - the module has been removed as the chat service is no longer available (https://github.com/ansible-collections/community.general/pull/10883)."
- "facter - the module has been replaced by ``community.general.facter_facts`` (https://github.com/ansible-collections/community.general/pull/10883)."
- "pacemaker_cluster - the option ``state`` is now required (https://github.com/ansible-collections/community.general/pull/10883)."
- >-
opkg - the value ``""`` for the option ``force`` is no longer allowed. Omit ``force`` instead (https://github.com/ansible-collections/community.general/pull/10883).
- "cmd_runner_fmt module utils - the parameter ``ctx_ignore_none`` to argument formatters has been removed (https://github.com/ansible-collections/community.general/pull/10883)."
- "cmd_runner module utils - the parameter ``ignore_value_none`` to ``CmdRunner.__call__()`` has been removed (https://github.com/ansible-collections/community.general/pull/10883)."
- >-
mh.deco module utils - the parameters ``on_success`` and ``on_failure`` of ``cause()`` have been removed; use ``when="success"`` and ``when="failure"`` instead (https://github.com/ansible-collections/community.general/pull/10883).
breaking_changes:
- "slack - the default of ``prepend_hash`` changed from ``auto`` to ``never`` (https://github.com/ansible-collections/community.general/pull/10883)."
- "mh.base module utils - ``debug`` will now always be delegated to the underlying ``AnsibleModule`` object (https://github.com/ansible-collections/community.general/pull/10883)."

View file

@ -5,7 +5,7 @@
namespace: community namespace: community
name: general name: general
version: 11.4.0 version: 12.0.0
readme: README.md readme: README.md
authors: authors:
- Ansible (https://github.com/ansible) - Ansible (https://github.com/ansible)

View file

@ -100,7 +100,7 @@ plugin_routing:
warning_text: Use the 'default' callback plugin with 'display_failed_stderr warning_text: Use the 'default' callback plugin with 'display_failed_stderr
= yes' option. = yes' option.
yaml: yaml:
deprecation: tombstone:
removal_version: 12.0.0 removal_version: 12.0.0
warning_text: >- warning_text: >-
The plugin has been superseded by the option `result_format=yaml` in callback plugin ansible.builtin.default from ansible-core 2.13 onwards. The plugin has been superseded by the option `result_format=yaml` in callback plugin ansible.builtin.default from ansible-core 2.13 onwards.
@ -153,7 +153,7 @@ plugin_routing:
removal_version: 13.0.0 removal_version: 13.0.0
warning_text: Project Atomic was sunset by the end of 2019. warning_text: Project Atomic was sunset by the end of 2019.
bearychat: bearychat:
deprecation: tombstone:
removal_version: 12.0.0 removal_version: 12.0.0
warning_text: Chat service is no longer available. warning_text: Chat service is no longer available.
catapult: catapult:
@ -257,7 +257,7 @@ plugin_routing:
docker_volume_info: docker_volume_info:
redirect: community.docker.docker_volume_info redirect: community.docker.docker_volume_info
facter: facter:
deprecation: tombstone:
removal_version: 12.0.0 removal_version: 12.0.0
warning_text: Use community.general.facter_facts instead. warning_text: Use community.general.facter_facts instead.
flowdock: flowdock:
@ -1084,7 +1084,7 @@ plugin_routing:
removal_version: 15.0.0 removal_version: 15.0.0
warning_text: The proxmox content has been moved to community.proxmox. warning_text: The proxmox content has been moved to community.proxmox.
purestorage: purestorage:
deprecation: tombstone:
removal_version: 12.0.0 removal_version: 12.0.0
warning_text: The modules for purestorage were removed in community.general 3.0.0, this document fragment was left behind. warning_text: The modules for purestorage were removed in community.general 3.0.0, this document fragment was left behind.
rackspace: rackspace:
@ -1121,7 +1121,7 @@ plugin_routing:
removal_version: 15.0.0 removal_version: 15.0.0
warning_text: The proxmox content has been moved to community.proxmox. warning_text: The proxmox content has been moved to community.proxmox.
pure: pure:
deprecation: tombstone:
removal_version: 12.0.0 removal_version: 12.0.0
warning_text: The modules for purestorage were removed in community.general 3.0.0, this module util was left behind. warning_text: The modules for purestorage were removed in community.general 3.0.0, this module util was left behind.
rax: rax:

View file

@ -1,195 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017 Ansible Project
# 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
# Make coding more python3-ish
from __future__ import annotations
DOCUMENTATION = r"""
author: Unknown (!UNKNOWN)
name: yaml
type: stdout
short_description: YAML-ized Ansible screen output
deprecated:
removed_in: 12.0.0
why: Starting in ansible-core 2.13, the P(ansible.builtin.default#callback) callback has support for printing output in
YAML format.
alternative: Use O(ansible.builtin.default#callback:result_format=yaml).
description:
- Ansible output that can be quite a bit easier to read than the default JSON formatting.
extends_documentation_fragment:
- default_callback
requirements:
- set as stdout in configuration
seealso:
- plugin: ansible.builtin.default
plugin_type: callback
description: >-
There is a parameter O(ansible.builtin.default#callback:result_format) in P(ansible.builtin.default#callback) that allows
you to change the output format to YAML.
notes:
- With ansible-core 2.13 or newer, you can instead specify V(yaml) for the parameter O(ansible.builtin.default#callback:result_format)
in P(ansible.builtin.default#callback).
"""
import yaml
import json
import re
import string
from collections.abc import Mapping, Sequence
from ansible.module_utils.common.text.converters import to_text
from ansible.plugins.callback import strip_internal_keys, module_response_deepcopy
from ansible.plugins.callback.default import CallbackModule as Default
# from http://stackoverflow.com/a/15423007/115478
def should_use_block(value):
"""Returns true if string should be in block format"""
for c in "\u000a\u000d\u001c\u001d\u001e\u0085\u2028\u2029":
if c in value:
return True
return False
def adjust_str_value_for_block(value):
# we care more about readable than accuracy, so...
# ...no trailing space
value = value.rstrip()
# ...and non-printable characters
value = ''.join(x for x in value if x in string.printable or ord(x) >= 0xA0)
# ...tabs prevent blocks from expanding
value = value.expandtabs()
# ...and odd bits of whitespace
value = re.sub(r'[\x0b\x0c\r]', '', value)
# ...as does trailing space
value = re.sub(r' +\n', '\n', value)
return value
def create_string_node(tag, value, style, default_style):
if style is None:
if should_use_block(value):
style = '|'
value = adjust_str_value_for_block(value)
else:
style = default_style
return yaml.representer.ScalarNode(tag, value, style=style)
try:
from ansible.module_utils.common.yaml import HAS_LIBYAML
# import below was added in https://github.com/ansible/ansible/pull/85039,
# first contained in ansible-core 2.19.0b2:
from ansible.utils.vars import transform_to_native_types
if HAS_LIBYAML:
from yaml.cyaml import CSafeDumper as SafeDumper
else:
from yaml import SafeDumper
class MyDumper(SafeDumper):
def represent_scalar(self, tag, value, style=None):
"""Uses block style for multi-line strings"""
node = create_string_node(tag, value, style, self.default_style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
return node
except ImportError:
# In case transform_to_native_types cannot be imported, we either have ansible-core 2.19.0b1
# (or some random commit from the devel or stable-2.19 branch after merging the DT changes
# and before transform_to_native_types was added), or we have a version without the DT changes.
# Here we simply assume we have a version without the DT changes, and thus can continue as
# with ansible-core 2.18 and before.
transform_to_native_types = None
from ansible.parsing.yaml.dumper import AnsibleDumper
class MyDumper(AnsibleDumper): # pylint: disable=inherit-non-class
def represent_scalar(self, tag, value, style=None):
"""Uses block style for multi-line strings"""
node = create_string_node(tag, value, style, self.default_style)
if self.alias_key is not None:
self.represented_objects[self.alias_key] = node
return node
def transform_recursively(value, transform):
# Since 2.19.0b7, this should no longer be needed:
# https://github.com/ansible/ansible/issues/85325
# https://github.com/ansible/ansible/pull/85389
if isinstance(value, Mapping):
return {transform(k): transform(v) for k, v in value.items()}
if isinstance(value, Sequence) and not isinstance(value, (str, bytes)):
return [transform(e) for e in value]
return transform(value)
class CallbackModule(Default):
"""
Variation of the Default output which uses nicely readable YAML instead
of JSON for printing results.
"""
CALLBACK_VERSION = 2.0
CALLBACK_TYPE = 'stdout'
CALLBACK_NAME = 'community.general.yaml'
def __init__(self):
super(CallbackModule, self).__init__()
def _dump_results(self, result, indent=None, sort_keys=True, keep_invocation=False):
if result.get('_ansible_no_log', False):
return json.dumps(dict(censored="The output has been hidden due to the fact that 'no_log: true' was specified for this result"))
# All result keys stating with _ansible_ are internal, so remove them from the result before we output anything.
abridged_result = strip_internal_keys(module_response_deepcopy(result))
# remove invocation unless specifically wanting it
if not keep_invocation and self._display.verbosity < 3 and 'invocation' in result:
del abridged_result['invocation']
# remove diff information from screen output
if self._display.verbosity < 3 and 'diff' in result:
del abridged_result['diff']
# remove exception from screen output
if 'exception' in abridged_result:
del abridged_result['exception']
dumped = ''
# put changed and skipped into a header line
if 'changed' in abridged_result:
dumped += f"changed={str(abridged_result['changed']).lower()} "
del abridged_result['changed']
if 'skipped' in abridged_result:
dumped += f"skipped={str(abridged_result['skipped']).lower()} "
del abridged_result['skipped']
# if we already have stdout, we don't need stdout_lines
if 'stdout' in abridged_result and 'stdout_lines' in abridged_result:
abridged_result['stdout_lines'] = '<omitted>'
# if we already have stderr, we don't need stderr_lines
if 'stderr' in abridged_result and 'stderr_lines' in abridged_result:
abridged_result['stderr_lines'] = '<omitted>'
if abridged_result:
dumped += '\n'
if transform_to_native_types is not None:
abridged_result = transform_recursively(abridged_result, lambda v: transform_to_native_types(v, redact=False))
dumped += to_text(yaml.dump(abridged_result, allow_unicode=True, width=1000, Dumper=MyDumper, default_flow_style=False))
# indent by a couple of spaces
dumped = '\n '.join(dumped.split('\n')).rstrip()
return dumped
def _serialize_diff(self, diff):
return to_text(yaml.dump(diff, allow_unicode=True, width=1000, Dumper=AnsibleDumper, default_flow_style=False))

View file

@ -1,51 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Simon Dodsley <simon@purestorage.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
class ModuleDocFragment(object):
# Documentation fragment for FlashBlade
FB = r"""
options:
fb_url:
description:
- FlashBlade management IP address or Hostname.
type: str
api_token:
description:
- FlashBlade API token for admin privileged user.
type: str
notes:
- This module requires the C(purity_fb) Python library.
- You must set E(PUREFB_URL) and E(PUREFB_API) environment variables if O(fb_url) and O(api_token) arguments are not passed
to the module directly.
requirements:
- purity_fb >= 1.1
"""
# Documentation fragment for FlashArray
FA = r"""
options:
fa_url:
description:
- FlashArray management IPv4 address or Hostname.
type: str
required: true
api_token:
description:
- FlashArray API token for admin privileged user.
type: str
required: true
notes:
- This module requires the C(purestorage) Python library.
- You must set E(PUREFA_URL) and E(PUREFA_API) environment variables if O(fa_url) and O(api_token) arguments are not passed
to the module directly.
requirements:
- purestorage
"""

View file

@ -132,16 +132,7 @@ class CmdRunner(object):
def binary(self): def binary(self):
return self.command[0] return self.command[0]
# remove parameter ignore_value_none in community.general 12.0.0 def __call__(self, args_order=None, output_process=None, check_mode_skip=False, check_mode_return=None, **kwargs):
def __call__(self, args_order=None, output_process=None, ignore_value_none=None, check_mode_skip=False, check_mode_return=None, **kwargs):
if ignore_value_none is None:
ignore_value_none = True
else:
self.module.deprecate(
"Using ignore_value_none when creating the runner context is now deprecated, "
"and the parameter will be removed in community.general 12.0.0. ",
version="12.0.0", collection_name="community.general"
)
if output_process is None: if output_process is None:
output_process = _process_as_is output_process = _process_as_is
if args_order is None: if args_order is None:
@ -153,7 +144,6 @@ class CmdRunner(object):
return _CmdRunnerContext(runner=self, return _CmdRunnerContext(runner=self,
args_order=args_order, args_order=args_order,
output_process=output_process, output_process=output_process,
ignore_value_none=ignore_value_none, # DEPRECATION: remove in community.general 12.0.0
check_mode_skip=check_mode_skip, check_mode_skip=check_mode_skip,
check_mode_return=check_mode_return, **kwargs) check_mode_return=check_mode_return, **kwargs)
@ -165,12 +155,10 @@ class CmdRunner(object):
class _CmdRunnerContext(object): class _CmdRunnerContext(object):
def __init__(self, runner, args_order, output_process, ignore_value_none, check_mode_skip, check_mode_return, **kwargs): def __init__(self, runner, args_order, output_process, check_mode_skip, check_mode_return, **kwargs):
self.runner = runner self.runner = runner
self.args_order = tuple(args_order) self.args_order = tuple(args_order)
self.output_process = output_process self.output_process = output_process
# DEPRECATION: parameter ignore_value_none at the context level is deprecated and will be removed in community.general 12.0.0
self.ignore_value_none = ignore_value_none
self.check_mode_skip = check_mode_skip self.check_mode_skip = check_mode_skip
self.check_mode_return = check_mode_return self.check_mode_return = check_mode_return
self.run_command_args = dict(kwargs) self.run_command_args = dict(kwargs)
@ -209,8 +197,7 @@ class _CmdRunnerContext(object):
value = named_args[arg_name] value = named_args[arg_name]
elif not runner.arg_formats[arg_name].ignore_missing_value: elif not runner.arg_formats[arg_name].ignore_missing_value:
raise MissingArgumentValue(self.args_order, arg_name) raise MissingArgumentValue(self.args_order, arg_name)
# DEPRECATION: remove parameter ctx_ignore_none in 12.0.0 self.cmd.extend(runner.arg_formats[arg_name](value))
self.cmd.extend(runner.arg_formats[arg_name](value, ctx_ignore_none=self.ignore_value_none))
except MissingArgumentValue: except MissingArgumentValue:
raise raise
except Exception as e: except Exception as e:
@ -226,7 +213,6 @@ class _CmdRunnerContext(object):
@property @property
def run_info(self): def run_info(self):
return dict( return dict(
ignore_value_none=self.ignore_value_none, # DEPRECATION: remove in community.general 12.0.0
check_rc=self.check_rc, check_rc=self.check_rc,
environ_update=self.environ_update, environ_update=self.environ_update,
args_order=self.args_order, args_order=self.args_order,

View file

@ -16,16 +16,13 @@ def _ensure_list(value):
class _ArgFormat(object): class _ArgFormat(object):
# DEPRECATION: set default value for ignore_none to True in community.general 12.0.0 def __init__(self, func, ignore_none=True, ignore_missing_value=False):
def __init__(self, func, ignore_none=None, ignore_missing_value=False):
self.func = func self.func = func
self.ignore_none = ignore_none self.ignore_none = ignore_none
self.ignore_missing_value = ignore_missing_value self.ignore_missing_value = ignore_missing_value
# DEPRECATION: remove parameter ctx_ignore_none in community.general 12.0.0 def __call__(self, value):
def __call__(self, value, ctx_ignore_none=True): ignore_none = self.ignore_none if self.ignore_none is not None else True
# DEPRECATION: replace ctx_ignore_none with True in community.general 12.0.0
ignore_none = self.ignore_none if self.ignore_none is not None else ctx_ignore_none
if value is None and ignore_none: if value is None and ignore_none:
return [] return []
f = self.func f = self.func

View file

@ -15,9 +15,8 @@ from ansible_collections.community.general.plugins.module_utils.mh.deco import m
class ModuleHelperBase(object): class ModuleHelperBase(object):
module = None module = None
ModuleHelperException = _MHE ModuleHelperException = _MHE
# in 12.0.0 add 'debug' to the tuple
_delegated_to_module = ( _delegated_to_module = (
'check_mode', 'get_bin_path', 'warn', 'deprecate', 'check_mode', 'get_bin_path', 'warn', 'deprecate', 'debug',
) )
def __init__(self, module=None): def __init__(self, module=None):
@ -29,18 +28,6 @@ class ModuleHelperBase(object):
if not isinstance(self.module, AnsibleModule): if not isinstance(self.module, AnsibleModule):
self.module = AnsibleModule(**self.module) self.module = AnsibleModule(**self.module)
# in 12.0.0 remove this if statement entirely
if hasattr(self, 'debug'):
msg = (
"This class ({cls}) has an attribute 'debug' defined and that is deprecated. "
"Method 'debug' will be an integral part of ModuleHelper in community.general "
"12.0.0, delegated to the underlying AnsibleModule object. "
"Please rename the existing attribute to prevent this message from showing.".format(cls=self.__class__.__name__)
)
self.deprecate(msg, version="12.0.0", collection_name="community.general")
else:
self._delegated_to_module = self._delegated_to_module + ('debug',)
@property @property
def diff_mode(self): def diff_mode(self):
return self.module._diff return self.module._diff

View file

@ -13,22 +13,16 @@ from functools import wraps
from ansible_collections.community.general.plugins.module_utils.mh.exceptions import ModuleHelperException from ansible_collections.community.general.plugins.module_utils.mh.exceptions import ModuleHelperException
def cause_changes(on_success=None, on_failure=None, when=None): def cause_changes(when=None):
# Parameters on_success and on_failure are deprecated and should be removed in community.general 12.0.0
def deco(func): def deco(func):
@wraps(func) @wraps(func)
def wrapper(self, *args, **kwargs): def wrapper(self, *args, **kwargs):
try: try:
func(self, *args, **kwargs) func(self, *args, **kwargs)
if on_success is not None: if when == "success":
self.changed = on_success
elif when == "success":
self.changed = True self.changed = True
except Exception: except Exception:
if on_failure is not None: if when == "failure":
self.changed = on_failure
elif when == "failure":
self.changed = True self.changed = True
raise raise
finally: finally:

View file

@ -1,115 +0,0 @@
# -*- coding: utf-8 -*-
# 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.
#
# Copyright (c), Simon Dodsley <simon@purestorage.com>,2017
# Simplified BSD License (see LICENSES/BSD-2-Clause.txt or https://opensource.org/licenses/BSD-2-Clause)
# SPDX-License-Identifier: BSD-2-Clause
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
HAS_PURESTORAGE = True
try:
from purestorage import purestorage
except ImportError:
HAS_PURESTORAGE = False
HAS_PURITY_FB = True
try:
from purity_fb import PurityFb, FileSystem, FileSystemSnapshot, SnapshotSuffix, rest # noqa: F401, pylint: disable=unused-import
except ImportError:
HAS_PURITY_FB = False
# (TODO: remove next line!)
from functools import wraps # noqa: F401, pylint: disable=unused-import
from os import environ
# (TODO: remove next line!)
from os import path # noqa: F401, pylint: disable=unused-import
import platform
VERSION = 1.2
USER_AGENT_BASE = 'Ansible'
API_AGENT_VERSION = 1.5
def get_system(module):
"""Return System Object or Fail"""
user_agent = '%(base)s %(class)s/%(version)s (%(platform)s)' % {
'base': USER_AGENT_BASE,
'class': __name__,
'version': VERSION,
'platform': platform.platform()
}
array_name = module.params['fa_url']
api = module.params['api_token']
if array_name and api:
system = purestorage.FlashArray(array_name, api_token=api, user_agent=user_agent)
elif environ.get('PUREFA_URL') and environ.get('PUREFA_API'):
system = purestorage.FlashArray(environ.get('PUREFA_URL'), api_token=(environ.get('PUREFA_API')), user_agent=user_agent)
else:
module.fail_json(msg="You must set PUREFA_URL and PUREFA_API environment variables or the fa_url and api_token module arguments")
try:
system.get()
except Exception:
module.fail_json(msg="Pure Storage FlashArray authentication failed. Check your credentials")
return system
def get_blade(module):
"""Return System Object or Fail"""
user_agent = '%(base)s %(class)s/%(version)s (%(platform)s)' % {
'base': USER_AGENT_BASE,
'class': __name__,
'version': VERSION,
'platform': platform.platform()
}
blade_name = module.params['fb_url']
api = module.params['api_token']
if blade_name and api:
blade = PurityFb(blade_name)
blade.disable_verify_ssl()
try:
blade.login(api)
versions = blade.api_version.list_versions().versions
if API_AGENT_VERSION in versions:
blade._api_client.user_agent = user_agent
except rest.ApiException as e:
module.fail_json(msg="Pure Storage FlashBlade authentication failed. Check your credentials")
elif environ.get('PUREFB_URL') and environ.get('PUREFB_API'):
blade = PurityFb(environ.get('PUREFB_URL'))
blade.disable_verify_ssl()
try:
blade.login(environ.get('PUREFB_API'))
versions = blade.api_version.list_versions().versions
if API_AGENT_VERSION in versions:
blade._api_client.user_agent = user_agent
except rest.ApiException as e:
module.fail_json(msg="Pure Storage FlashBlade authentication failed. Check your credentials")
else:
module.fail_json(msg="You must set PUREFB_URL and PUREFB_API environment variables or the fb_url and api_token module arguments")
return blade
def purefa_argument_spec():
"""Return standard base dictionary used for the argument_spec argument in AnsibleModule"""
return dict(
fa_url=dict(),
api_token=dict(no_log=True),
)
def purefb_argument_spec():
"""Return standard base dictionary used for the argument_spec argument in AnsibleModule"""
return dict(
fb_url=dict(),
api_token=dict(no_log=True),
)

View file

@ -1,176 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2016, Jiangge Zhang <tonyseek@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"""
module: bearychat
short_description: Send BearyChat notifications
description:
- The M(community.general.bearychat) module sends notifications to U(https://bearychat.com) using the Incoming Robot integration.
deprecated:
removed_in: 12.0.0
why: Chat service is no longer available.
alternative: There is none.
author: "Jiangge Zhang (@tonyseek)"
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: none
diff_mode:
support: none
options:
url:
type: str
description:
- BearyChat WebHook URL. This authenticates you to the bearychat service. It looks like
V(https://hook.bearychat.com/=ae2CF/incoming/e61bd5c57b164e04b11ac02e66f47f60).
required: true
text:
type: str
description:
- Message to send.
markdown:
description:
- If V(true), text is parsed as markdown.
default: true
type: bool
channel:
type: str
description:
- Channel to send the message to. If absent, the message goes to the default channel selected by the O(url).
attachments:
type: list
elements: dict
description:
- Define a list of attachments. For more information, see
U(https://github.com/bearyinnovative/bearychat-tutorial/blob/master/robots/incoming.md#attachments).
"""
EXAMPLES = r"""
- name: Send notification message via BearyChat
local_action:
module: bearychat
url: |
https://hook.bearychat.com/=ae2CF/incoming/e61bd5c57b164e04b11ac02e66f47f60
text: "{{ inventory_hostname }} completed"
- name: Send notification message via BearyChat all options
local_action:
module: bearychat
url: |
https://hook.bearychat.com/=ae2CF/incoming/e61bd5c57b164e04b11ac02e66f47f60
text: "{{ inventory_hostname }} completed"
markdown: false
channel: "#ansible"
attachments:
- title: "Ansible on {{ inventory_hostname }}"
text: "May the Force be with you."
color: "#ffffff"
images:
- http://example.com/index.png
"""
RETURN = r"""
msg:
description: Execution result.
returned: success
type: str
sample: "OK"
"""
try:
from ansible.module_utils.six.moves.urllib.parse import urlparse, urlunparse
HAS_URLPARSE = True
except Exception:
HAS_URLPARSE = False
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.urls import fetch_url
def build_payload_for_bearychat(module, text, markdown, channel, attachments):
payload = {}
if text is not None:
payload['text'] = text
if markdown is not None:
payload['markdown'] = markdown
if channel is not None:
payload['channel'] = channel
if attachments is not None:
payload.setdefault('attachments', []).extend(
build_payload_for_bearychat_attachment(
module, item.get('title'), item.get('text'), item.get('color'),
item.get('images'))
for item in attachments)
payload = 'payload=%s' % module.jsonify(payload)
return payload
def build_payload_for_bearychat_attachment(module, title, text, color, images):
attachment = {}
if title is not None:
attachment['title'] = title
if text is not None:
attachment['text'] = text
if color is not None:
attachment['color'] = color
if images is not None:
target_images = attachment.setdefault('images', [])
if not isinstance(images, (list, tuple)):
images = [images]
for image in images:
if isinstance(image, dict) and 'url' in image:
image = {'url': image['url']}
elif hasattr(image, 'startswith') and image.startswith('http'):
image = {'url': image}
else:
module.fail_json(
msg="BearyChat doesn't have support for this kind of "
"attachment image")
target_images.append(image)
return attachment
def do_notify_bearychat(module, url, payload):
response, info = fetch_url(module, url, data=payload)
if info['status'] != 200:
url_info = urlparse(url)
obscured_incoming_webhook = urlunparse(
(url_info.scheme, url_info.netloc, '[obscured]', '', '', ''))
module.fail_json(
msg=" failed to send %s to %s: %s" % (
payload, obscured_incoming_webhook, info['msg']))
def main():
module = AnsibleModule(argument_spec={
'url': dict(type='str', required=True, no_log=True),
'text': dict(type='str'),
'markdown': dict(default=True, type='bool'),
'channel': dict(type='str'),
'attachments': dict(type='list', elements='dict'),
})
if not HAS_URLPARSE:
module.fail_json(msg='urlparse is not installed')
url = module.params['url']
text = module.params['text']
markdown = module.params['markdown']
channel = module.params['channel']
attachments = module.params['attachments']
payload = build_payload_for_bearychat(
module, text, markdown, channel, attachments)
do_notify_bearychat(module, url, payload)
module.exit_json(msg="OK")
if __name__ == '__main__':
main()

View file

@ -1,82 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2012, 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"""
module: facter
short_description: Runs the discovery program C(facter) on the remote system
description:
- Runs the C(facter) discovery program (U(https://github.com/puppetlabs/facter)) on the remote system, returning JSON data
that can be useful for inventory purposes.
deprecated:
removed_in: 12.0.0
why: The module has been replaced by M(community.general.facter_facts).
alternative: Use M(community.general.facter_facts) instead.
extends_documentation_fragment:
- community.general.attributes
attributes:
check_mode:
support: none
diff_mode:
support: none
options:
arguments:
description:
- Specifies arguments for facter.
type: list
elements: str
requirements:
- facter
- ruby-json
author:
- Ansible Core Team
- Michael DeHaan
"""
EXAMPLES = r"""
# Example command-line invocation
# ansible www.example.net -m facter
- name: Execute facter no arguments
community.general.facter:
- name: Execute facter with arguments
community.general.facter:
arguments:
- -p
- system_uptime
- timezone
- is_virtual
"""
import json
from ansible.module_utils.basic import AnsibleModule
def main():
module = AnsibleModule(
argument_spec=dict(
arguments=dict(type='list', elements='str')
)
)
facter_path = module.get_bin_path(
'facter',
opt_dirs=['/opt/puppetlabs/bin'])
cmd = [facter_path, "--json"]
if module.params['arguments']:
cmd += module.params['arguments']
rc, out, err = module.run_command(cmd, check_rc=True)
module.exit_json(**json.loads(out))
if __name__ == '__main__':
main()

View file

@ -13,7 +13,7 @@ DOCUMENTATION = r"""
module: ohai module: ohai
short_description: Returns inventory data from I(Ohai) short_description: Returns inventory data from I(Ohai)
description: description:
- Similar to the M(community.general.facter) module, this runs the I(Ohai) discovery program (U(https://docs.chef.io/ohai.html)) - Similar to the M(community.general.facter_facts) module, this runs the I(Ohai) discovery program (U(https://docs.chef.io/ohai.html))
on the remote host and returns JSON inventory data. I(Ohai) data is a bit more verbose and nested than I(facter). on the remote host and returns JSON inventory data. I(Ohai) data is a bit more verbose and nested than I(facter).
extends_documentation_fragment: extends_documentation_fragment:
- community.general.attributes - community.general.attributes

View file

@ -43,10 +43,7 @@ options:
force: force:
description: description:
- The C(opkg --force) parameter used. - The C(opkg --force) parameter used.
- State V("") is deprecated and will be removed in community.general 12.0.0. Please omit the parameter O(force) to obtain
the same behavior.
choices: choices:
- ""
- "depends" - "depends"
- "maintainer" - "maintainer"
- "reinstall" - "reinstall"
@ -128,7 +125,7 @@ class Opkg(StateModuleHelper):
argument_spec=dict( argument_spec=dict(
name=dict(aliases=["pkg"], required=True, type="list", elements="str"), name=dict(aliases=["pkg"], required=True, type="list", elements="str"),
state=dict(default="present", choices=["present", "installed", "absent", "removed"]), state=dict(default="present", choices=["present", "installed", "absent", "removed"]),
force=dict(choices=["", "depends", "maintainer", "reinstall", "overwrite", "downgrade", "space", force=dict(choices=["depends", "maintainer", "reinstall", "overwrite", "downgrade", "space",
"postinstall", "remove", "checksum", "removal-of-dependent-packages"]), "postinstall", "remove", "checksum", "removal-of-dependent-packages"]),
update_cache=dict(default=False, type='bool'), update_cache=dict(default=False, type='bool'),
executable=dict(type="path"), executable=dict(type="path"),
@ -147,15 +144,6 @@ class Opkg(StateModuleHelper):
removed="remove", removed="remove",
) )
def _force(value):
# 12.0.0 function _force() to be removed entirely
if value == "":
self.deprecate('Value "" is deprecated. Simply omit the parameter "force" to prevent any --force-X argument when running opkg',
version="12.0.0",
collection_name="community.general")
value = None
return cmd_runner_fmt.as_optval("--force-")(value, ctx_ignore_none=True)
dir, cmd = os.path.split(self.vars.executable) if self.vars.executable else (None, "opkg") dir, cmd = os.path.split(self.vars.executable) if self.vars.executable else (None, "opkg")
self.runner = CmdRunner( self.runner = CmdRunner(
@ -164,7 +152,7 @@ class Opkg(StateModuleHelper):
arg_formats=dict( arg_formats=dict(
package=cmd_runner_fmt.as_list(), package=cmd_runner_fmt.as_list(),
state=cmd_runner_fmt.as_map(state_map), state=cmd_runner_fmt.as_map(state_map),
force=cmd_runner_fmt.as_func(_force), # 12.0.0 replace with cmd_runner_fmt.as_optval("--force-") force=cmd_runner_fmt.as_optval("--force-"),
update_cache=cmd_runner_fmt.as_bool("update"), update_cache=cmd_runner_fmt.as_bool("update"),
version=cmd_runner_fmt.as_fixed("--version"), version=cmd_runner_fmt.as_fixed("--version"),
), ),

View file

@ -30,6 +30,7 @@ options:
- The value V(maintenance) has been added in community.general 11.1.0. - The value V(maintenance) has been added in community.general 11.1.0.
choices: [cleanup, offline, online, restart, maintenance] choices: [cleanup, offline, online, restart, maintenance]
type: str type: str
required: true
name: name:
description: description:
- Specify which node of the cluster you want to manage. V(null) == the cluster status itself, V(all) == check the status - Specify which node of the cluster you want to manage. V(null) == the cluster status itself, V(all) == check the status
@ -74,7 +75,7 @@ class PacemakerCluster(StateModuleHelper):
module = dict( module = dict(
argument_spec=dict( argument_spec=dict(
state=dict(type='str', choices=[ state=dict(type='str', choices=[
'cleanup', 'offline', 'online', 'restart', 'maintenance']), 'cleanup', 'offline', 'online', 'restart', 'maintenance'], required=True),
name=dict(type='str', aliases=['node']), name=dict(type='str', aliases=['node']),
timeout=dict(type='int', default=300), timeout=dict(type='int', default=300),
force=dict(type='bool', default=True) force=dict(type='bool', default=True)
@ -106,13 +107,6 @@ class PacemakerCluster(StateModuleHelper):
collection_name='community.general' collection_name='community.general'
) )
if not self.module.params['state']:
self.module.deprecate(
'Parameter "state" values not set is being deprecated. Make sure to provide a value for "state"',
version='12.0.0',
collection_name='community.general'
)
def __quit_module__(self): def __quit_module__(self):
self.vars.set('value', self._get()['out']) self.vars.set('value', self._get()['out'])

View file

@ -143,10 +143,10 @@ options:
prefixes only cover a small set of the prefixes that should not have a V(#) prepended. Since an exact condition which prefixes only cover a small set of the prefixes that should not have a V(#) prepended. Since an exact condition which
O(channel) values must not have the V(#) prefix is not known, the value V(auto) for this option is deprecated in the O(channel) values must not have the V(#) prefix is not known, the value V(auto) for this option is deprecated in the
future. It is best to explicitly set O(prepend_hash=always) or O(prepend_hash=never) to obtain the needed behavior. future. It is best to explicitly set O(prepend_hash=always) or O(prepend_hash=never) to obtain the needed behavior.
- The B(current default) is V(auto), which has been B(deprecated) since community.general 10.2.0. It is going to change - Before community.general 12.0.0, the default was V(auto). It has been deprecated since community.general 10.2.0.
to V(never) in community.general 12.0.0. To prevent deprecation warnings you can explicitly set O(prepend_hash) to - Note that V(auto) will be deprecated in a future version.
the value you want. We suggest to only use V(always) or V(never), but not V(auto), when explicitly setting a value. # TODO: Deprecate 'auto' in community.general 13.0.0
# when the default changes in community.general 12.0.0, add deprecation for the `auto` value for 14.0.0 default: never
choices: choices:
- 'always' - 'always'
- 'never' - 'never'
@ -466,7 +466,7 @@ def main():
attachments=dict(type='list', elements='dict'), attachments=dict(type='list', elements='dict'),
blocks=dict(type='list', elements='dict'), blocks=dict(type='list', elements='dict'),
message_id=dict(type='str'), message_id=dict(type='str'),
prepend_hash=dict(type='str', choices=['always', 'never', 'auto']), prepend_hash=dict(type='str', choices=['always', 'never', 'auto'], default='never'),
), ),
supports_check_mode=True, supports_check_mode=True,
) )
@ -487,15 +487,6 @@ def main():
message_id = module.params['message_id'] message_id = module.params['message_id']
prepend_hash = module.params['prepend_hash'] prepend_hash = module.params['prepend_hash']
if prepend_hash is None:
module.deprecate(
"The default value 'auto' for 'prepend_hash' is deprecated and will change to 'never' in community.general 12.0.0."
" You can explicitly set 'prepend_hash' in your task to avoid this deprecation warning",
version="12.0.0",
collection_name="community.general",
)
prepend_hash = 'auto'
color_choices = ['normal', 'good', 'warning', 'danger'] color_choices = ['normal', 'good', 'warning', 'danger']
if color not in color_choices and not is_valid_hex_color(color): if color not in color_choices and not is_valid_hex_color(color):
module.fail_json(msg="Color value specified should be either one of %r " module.fail_json(msg="Color value specified should be either one of %r "

View file

@ -1,6 +0,0 @@
# Copyright (c) Ansible Project
# 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

View file

@ -1,7 +0,0 @@
---
# Copyright (c) Ansible Project
# 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
dependencies:
- setup_remote_tmp_dir

View file

@ -1,143 +0,0 @@
---
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Copyright (c) Ansible Project
# 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: Write vault password to disk
ansible.builtin.copy:
dest: "{{ remote_tmp_dir }}/vault-password"
content: asdf
- name: Run tests
include_role:
name: callback
vars:
tests:
- name: Basic run
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.yaml
playbook: |
- hosts: testhost
gather_facts: false
tasks:
- name: Sample task name
debug:
msg: sample debug msg
expected_output:
- ""
- "PLAY [testhost] ****************************************************************"
- ""
- "TASK [Sample task name] ********************************************************"
- "ok: [testhost] => "
- " msg: sample debug msg"
- ""
- "PLAY RECAP *********************************************************************"
- "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
- name: Test umlauts in multiline
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.yaml
playbook: |
- hosts: testhost
gather_facts: false
tasks:
- name: Umlaut output
debug:
msg: "äöü\néêè\nßï☺"
expected_output:
- ""
- "PLAY [testhost] ****************************************************************"
- ""
- "TASK [Umlaut output] ***********************************************************"
- "ok: [testhost] => "
- " msg: |-"
- " äöü"
- " éêè"
- " ßï☺"
- ""
- "PLAY RECAP *********************************************************************"
- "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
- name: Test to_yaml
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.yaml
playbook: !unsafe |
- hosts: testhost
gather_facts: false
vars:
data: |
line 1
line 2
line 3
tasks:
- name: Test to_yaml
debug:
msg: "{{ data | to_yaml }}"
expected_output:
- ""
- "PLAY [testhost] ****************************************************************"
- ""
- "TASK [Test to_yaml] ************************************************************"
- "ok: [testhost] => "
- " msg: |-"
- " 'line 1"
- " "
- " line 2"
- " "
- " line 3"
- " "
- " '"
- ""
- "PLAY RECAP *********************************************************************"
- "testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
- name: Some more fun with data tagging
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.yaml
extra_cli_arguments: "--vault-password-file {{ remote_tmp_dir }}/vault-password"
playbook: !unsafe |
- hosts: testhost
gather_facts: false
vars:
foo: bar
baz: !vault |
$ANSIBLE_VAULT;1.1;AES256
30393064316433636636373336363538663034643135363938646665393661353833633865313765
3835366434646339313337663335393865336163663434310a316161313662666466333332353731
64663064366461643162666137303737643164376134303034306366383830336232363837636638
3830653338626130360a313639623231353931356563313065373661303262646337383534663932
64353461663065333362346264326335373032313333343539646661656634653138646332313639
3566313765626464613734623664663266336237646139373935
tasks:
- name: Test regular string
debug:
var: foo
- name: Test vaulted string
debug:
var: baz
expected_output:
- ""
- "PLAY [testhost] ****************************************************************"
- ""
- "TASK [Test regular string] *****************************************************"
- "ok: [testhost] => "
- " foo: bar"
- ""
- "TASK [Test vaulted string] *****************************************************"
- "ok: [testhost] => "
- " baz: aBcDeFgHiJkLmNoPqRsTuVwXyZ012345"
- ""
- "PLAY RECAP *********************************************************************"
- "testhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "

View file

@ -205,7 +205,7 @@ TC_RUNNER = dict(
results="0-/-ni-/-nu" results="0-/-ni-/-nu"
), ),
), ),
aa_bb_ignore_none_with_none=( aa_bb_with_none=(
dict( dict(
args_bundle=dict( args_bundle=dict(
aa=dict(type="int", value=49, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"), aa=dict(type="int", value=49, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"),
@ -214,7 +214,6 @@ TC_RUNNER = dict(
runner_init_args=dict(default_args_order=['bb', 'aa']), runner_init_args=dict(default_args_order=['bb', 'aa']),
runner_ctx_args=dict( runner_ctx_args=dict(
args_order=['aa', 'bb'], args_order=['aa', 'bb'],
ignore_value_none=True, # default
), ),
), ),
dict(runner_ctx_run_args=dict(bb=None), rc=0, out="ni", err="nu"), dict(runner_ctx_run_args=dict(bb=None), rc=0, out="ni", err="nu"),
@ -224,25 +223,6 @@ TC_RUNNER = dict(
), ),
), ),
), ),
aa_bb_ignore_not_none_with_none=(
dict(
args_bundle=dict(
aa=dict(type="int", value=49, fmt_func=cmd_runner_fmt.as_opt_eq_val, fmt_arg="--answer"),
bb=dict(fmt_func=cmd_runner_fmt.as_bool, fmt_arg="--bb-here"),
),
runner_init_args=dict(default_args_order=['bb', 'aa']),
runner_ctx_args=dict(
args_order=['aa', 'bb'],
ignore_value_none=False,
),
),
dict(runner_ctx_run_args=dict(aa=None, bb=True), rc=0, out="ni", err="nu"),
dict(
run_info=dict(
cmd=['/mock/bin/testing', '--answer=None', '--bb-here'],
),
),
),
aa_bb_fixed=( aa_bb_fixed=(
dict( dict(
args_bundle=dict( args_bundle=dict(

View file

@ -23,12 +23,6 @@ CAUSE_CHG_DECO_PARAMS = ['deco_args', 'expect_exception', 'expect_changed']
CAUSE_CHG_DECO = dict( CAUSE_CHG_DECO = dict(
none_succ=dict(deco_args={}, expect_exception=False, expect_changed=None), none_succ=dict(deco_args={}, expect_exception=False, expect_changed=None),
none_fail=dict(deco_args={}, expect_exception=True, expect_changed=None), none_fail=dict(deco_args={}, expect_exception=True, expect_changed=None),
onsucc_succ=dict(deco_args=dict(on_success=True), expect_exception=False, expect_changed=True),
onsucc_fail=dict(deco_args=dict(on_success=True), expect_exception=True, expect_changed=None),
onfail_succ=dict(deco_args=dict(on_failure=True), expect_exception=False, expect_changed=None),
onfail_fail=dict(deco_args=dict(on_failure=True), expect_exception=True, expect_changed=True),
onboth_succ=dict(deco_args=dict(on_success=True, on_failure=True), expect_exception=False, expect_changed=True),
onboth_fail=dict(deco_args=dict(on_success=True, on_failure=True), expect_exception=True, expect_changed=True),
whensucc_succ=dict(deco_args=dict(when="success"), expect_exception=False, expect_changed=True), whensucc_succ=dict(deco_args=dict(when="success"), expect_exception=False, expect_changed=True),
whensucc_fail=dict(deco_args=dict(when="success"), expect_exception=True, expect_changed=None), whensucc_fail=dict(deco_args=dict(when="success"), expect_exception=True, expect_changed=None),
whenfail_succ=dict(deco_args=dict(when="failure"), expect_exception=False, expect_changed=None), whenfail_succ=dict(deco_args=dict(when="failure"), expect_exception=False, expect_changed=None),