mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Add subcommands parameter for module alternatives. (#4654)
* Add slaves parameter for module alternatives. * alternatives: Improve documentation abous slaves parameter * alternatives: Apply suggestions from code review Co-authored-by: Felix Fontein <felix@fontein.de> * alternatives: Add schangelog for slaves parameter * alernatives: Add integration tests * alternatives: Improv tests * alternatives: Update tests/integration/targets/alternatives/tasks/slaves.yml Co-authored-by: Felix Fontein <felix@fontein.de> * alternatives: Rework logic to support updating priority and subcommands * alternatives: Use more inclusive naming * alternatives: Fix linter warnings * alternatives: Dont fail if link is absent * alternatives: Update changelog fragment * alternatives: Add tests for prio change and removing * alternatives: Apply suggestions from code review Co-authored-by: Felix Fontein <felix@fontein.de> * alternatives: Add `state=auto`to reset mode to auto * alternatives: Fix linter warnings * alternatives: Fix documentation. * alternatives: Combine multiple messages. * alternatives: Set command env for all commands. * alternatives: Do not update subcommands if parameter is omited * alternatives: Fix a bug with python 2.7 var scoping * alternatives: Improce diff before generation * alternatives: Fix linter warnings * alternatives: Fix test names * alternatives: Simplify subcommands handling and improve diffs * aliases: Only test for subcommand changes if subcommands parameter is set. * Update plugins/modules/system/alternatives.py Co-authored-by: Felix Fontein <felix@fontein.de> * Apply suggestions from code review Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
parent
62ff263ac1
commit
373da56b5b
8 changed files with 420 additions and 106 deletions
2
.github/BOTMETA.yml
vendored
2
.github/BOTMETA.yml
vendored
|
@ -1027,7 +1027,7 @@ files:
|
||||||
$modules/system/alternatives.py:
|
$modules/system/alternatives.py:
|
||||||
maintainers: mulby
|
maintainers: mulby
|
||||||
labels: alternatives
|
labels: alternatives
|
||||||
ignore: DavidWittman
|
ignore: DavidWittman jiuka
|
||||||
$modules/system/aix_lvol.py:
|
$modules/system/aix_lvol.py:
|
||||||
maintainers: adejoux
|
maintainers: adejoux
|
||||||
$modules/system/awall.py:
|
$modules/system/awall.py:
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
minor_changes:
|
||||||
|
- alternatives - add ``subcommands`` parameter (https://github.com/ansible-collections/community.general/pull/4654).
|
||||||
|
- alternatives - add ``state=absent`` to be able to remove an alternative (https://github.com/ansible-collections/community.general/pull/4654).
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
# Copyright: (c) 2014, Gabe Mulley <gabe.mulley@gmail.com>
|
# Copyright: (c) 2014, Gabe Mulley <gabe.mulley@gmail.com>
|
||||||
# Copyright: (c) 2015, David Wittman <dwittman@gmail.com>
|
# Copyright: (c) 2015, David Wittman <dwittman@gmail.com>
|
||||||
|
# Copyright: (c) 2022, Marius Rieder <marius.rieder@scs.com>
|
||||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
from __future__ import absolute_import, division, print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
|
@ -17,6 +18,7 @@ description:
|
||||||
- Manages symbolic links using the 'update-alternatives' tool.
|
- Manages symbolic links using the 'update-alternatives' tool.
|
||||||
- Useful when multiple programs are installed but provide similar functionality (e.g. different editors).
|
- Useful when multiple programs are installed but provide similar functionality (e.g. different editors).
|
||||||
author:
|
author:
|
||||||
|
- Marius Rieder (@jiuka)
|
||||||
- David Wittman (@DavidWittman)
|
- David Wittman (@DavidWittman)
|
||||||
- Gabe Mulley (@mulby)
|
- Gabe Mulley (@mulby)
|
||||||
options:
|
options:
|
||||||
|
@ -47,10 +49,36 @@ options:
|
||||||
not set it as the currently selected alternative for the group.
|
not set it as the currently selected alternative for the group.
|
||||||
- C(selected) - install the alternative (if not already installed), and
|
- C(selected) - install the alternative (if not already installed), and
|
||||||
set it as the currently selected alternative for the group.
|
set it as the currently selected alternative for the group.
|
||||||
choices: [ present, selected ]
|
- C(auto) - install the alternative (if not already installed), and
|
||||||
|
set the group to auto mode. Added in community.general 5.1.0.
|
||||||
|
- C(absent) - removes the alternative. Added in community.general 5.1.0.
|
||||||
|
choices: [ present, selected, auto, absent ]
|
||||||
default: selected
|
default: selected
|
||||||
type: str
|
type: str
|
||||||
version_added: 4.8.0
|
version_added: 4.8.0
|
||||||
|
subcommands:
|
||||||
|
description:
|
||||||
|
- A list of subcommands.
|
||||||
|
- Each subcommand needs a name, a link and a path parameter.
|
||||||
|
type: list
|
||||||
|
elements: dict
|
||||||
|
aliases: ['slaves']
|
||||||
|
suboptions:
|
||||||
|
name:
|
||||||
|
description:
|
||||||
|
- The generic name of the subcommand.
|
||||||
|
type: str
|
||||||
|
required: true
|
||||||
|
path:
|
||||||
|
description:
|
||||||
|
- The path to the real executable that the subcommand should point to.
|
||||||
|
type: path
|
||||||
|
required: true
|
||||||
|
link:
|
||||||
|
description:
|
||||||
|
- The path to the symbolic link that should point to the real subcommand executable.
|
||||||
|
type: path
|
||||||
|
version_added: 5.1.0
|
||||||
requirements: [ update-alternatives ]
|
requirements: [ update-alternatives ]
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
@ -78,6 +106,23 @@ EXAMPLES = r'''
|
||||||
path: /usr/bin/python3.5
|
path: /usr/bin/python3.5
|
||||||
link: /usr/bin/python
|
link: /usr/bin/python
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
|
- name: Install Python 3.5 and reset selection to auto
|
||||||
|
community.general.alternatives:
|
||||||
|
name: python
|
||||||
|
path: /usr/bin/python3.5
|
||||||
|
link: /usr/bin/python
|
||||||
|
state: auto
|
||||||
|
|
||||||
|
- name: keytool is a subcommand of java
|
||||||
|
community.general.alternatives:
|
||||||
|
name: java
|
||||||
|
link: /usr/bin/java
|
||||||
|
path: /usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java
|
||||||
|
subcommands:
|
||||||
|
- name: keytool
|
||||||
|
link: /usr/bin/keytool
|
||||||
|
path: /usr/lib/jvm/java-7-openjdk-amd64/jre/bin/keytool
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -90,10 +135,235 @@ from ansible.module_utils.basic import AnsibleModule
|
||||||
class AlternativeState:
|
class AlternativeState:
|
||||||
PRESENT = "present"
|
PRESENT = "present"
|
||||||
SELECTED = "selected"
|
SELECTED = "selected"
|
||||||
|
ABSENT = "absent"
|
||||||
|
AUTO = "auto"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def to_list(cls):
|
def to_list(cls):
|
||||||
return [cls.PRESENT, cls.SELECTED]
|
return [cls.PRESENT, cls.SELECTED, cls.ABSENT, cls.AUTO]
|
||||||
|
|
||||||
|
|
||||||
|
class AlternativesModule(object):
|
||||||
|
_UPDATE_ALTERNATIVES = None
|
||||||
|
|
||||||
|
def __init__(self, module):
|
||||||
|
self.module = module
|
||||||
|
self.result = dict(changed=False, diff=dict(before=dict(), after=dict()))
|
||||||
|
self.module.run_command_environ_update = {'LC_ALL': 'C'}
|
||||||
|
self.messages = []
|
||||||
|
self.run()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mode_present(self):
|
||||||
|
return self.module.params.get('state') in [AlternativeState.PRESENT, AlternativeState.SELECTED, AlternativeState.AUTO]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mode_selected(self):
|
||||||
|
return self.module.params.get('state') == AlternativeState.SELECTED
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mode_auto(self):
|
||||||
|
return self.module.params.get('state') == AlternativeState.AUTO
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.parse()
|
||||||
|
|
||||||
|
if self.mode_present:
|
||||||
|
# Check if we need to (re)install
|
||||||
|
subcommands_parameter = self.module.params['subcommands']
|
||||||
|
if (
|
||||||
|
self.path not in self.current_alternatives or
|
||||||
|
self.current_alternatives[self.path].get('priority') != self.priority or
|
||||||
|
(subcommands_parameter is not None and (
|
||||||
|
not all(s in subcommands_parameter for s in self.current_alternatives[self.path].get('subcommands')) or
|
||||||
|
not all(s in self.current_alternatives[self.path].get('subcommands') for s in subcommands_parameter)
|
||||||
|
))
|
||||||
|
):
|
||||||
|
self.install()
|
||||||
|
|
||||||
|
# Check if we need to set the preference
|
||||||
|
if self.mode_selected and self.current_path != self.path:
|
||||||
|
self.set()
|
||||||
|
|
||||||
|
# Check if we need to reset to auto
|
||||||
|
if self.mode_auto and self.current_mode == 'manual':
|
||||||
|
self.auto()
|
||||||
|
else:
|
||||||
|
# Check if we need to uninstall
|
||||||
|
if self.path in self.current_alternatives:
|
||||||
|
self.remove()
|
||||||
|
|
||||||
|
self.result['msg'] = ' '.join(self.messages)
|
||||||
|
self.module.exit_json(**self.result)
|
||||||
|
|
||||||
|
def install(self):
|
||||||
|
if not os.path.exists(self.path):
|
||||||
|
self.module.fail_json(msg="Specified path %s does not exist" % self.path)
|
||||||
|
if not self.link:
|
||||||
|
self.module.fail_json(msg='Needed to install the alternative, but unable to do so as we are missing the link')
|
||||||
|
|
||||||
|
cmd = [self.UPDATE_ALTERNATIVES, '--install', self.link, self.name, self.path, str(self.priority)]
|
||||||
|
|
||||||
|
if self.subcommands is not None:
|
||||||
|
subcommands = [['--slave', subcmd['link'], subcmd['name'], subcmd['path']] for subcmd in self.subcommands]
|
||||||
|
cmd += [item for sublist in subcommands for item in sublist]
|
||||||
|
|
||||||
|
self.result['changed'] = True
|
||||||
|
self.messages.append("Install alternative '%s' for '%s'." % (self.path, self.name))
|
||||||
|
|
||||||
|
if not self.module.check_mode:
|
||||||
|
self.module.run_command(cmd, check_rc=True)
|
||||||
|
|
||||||
|
if self.module._diff:
|
||||||
|
self.result['diff']['after'] = dict(
|
||||||
|
state=AlternativeState.PRESENT,
|
||||||
|
path=self.path,
|
||||||
|
priority=self.priority,
|
||||||
|
link=self.link,
|
||||||
|
)
|
||||||
|
if self.subcommands:
|
||||||
|
self.result['diff']['after'].update(dict(
|
||||||
|
subcommands=self.subcommands
|
||||||
|
))
|
||||||
|
|
||||||
|
def remove(self):
|
||||||
|
cmd = [self.UPDATE_ALTERNATIVES, '--remove', self.name, self.path]
|
||||||
|
self.result['changed'] = True
|
||||||
|
self.messages.append("Remove alternative '%s' from '%s'." % (self.path, self.name))
|
||||||
|
|
||||||
|
if not self.module.check_mode:
|
||||||
|
self.module.run_command(cmd, check_rc=True)
|
||||||
|
|
||||||
|
if self.module._diff:
|
||||||
|
self.result['diff']['after'] = dict(state=AlternativeState.ABSENT)
|
||||||
|
|
||||||
|
def set(self):
|
||||||
|
cmd = [self.UPDATE_ALTERNATIVES, '--set', self.name, self.path]
|
||||||
|
self.result['changed'] = True
|
||||||
|
self.messages.append("Set alternative '%s' for '%s'." % (self.path, self.name))
|
||||||
|
|
||||||
|
if not self.module.check_mode:
|
||||||
|
self.module.run_command(cmd, check_rc=True)
|
||||||
|
|
||||||
|
if self.module._diff:
|
||||||
|
self.result['diff']['after']['state'] = AlternativeState.SELECTED
|
||||||
|
|
||||||
|
def auto(self):
|
||||||
|
cmd = [self.UPDATE_ALTERNATIVES, '--auto', self.name]
|
||||||
|
self.messages.append("Set alternative to auto for '%s'." % (self.name))
|
||||||
|
self.result['changed'] = True
|
||||||
|
|
||||||
|
if not self.module.check_mode:
|
||||||
|
self.module.run_command(cmd, check_rc=True)
|
||||||
|
|
||||||
|
if self.module._diff:
|
||||||
|
self.result['diff']['after']['state'] = AlternativeState.PRESENT
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self.module.params.get('name')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def path(self):
|
||||||
|
return self.module.params.get('path')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def link(self):
|
||||||
|
return self.module.params.get('link') or self.current_link
|
||||||
|
|
||||||
|
@property
|
||||||
|
def priority(self):
|
||||||
|
return self.module.params.get('priority')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def subcommands(self):
|
||||||
|
if self.module.params.get('subcommands') is not None:
|
||||||
|
return self.module.params.get('subcommands')
|
||||||
|
elif self.path in self.current_alternatives and self.current_alternatives[self.path].get('subcommands'):
|
||||||
|
return self.current_alternatives[self.path].get('subcommands')
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def UPDATE_ALTERNATIVES(self):
|
||||||
|
if self._UPDATE_ALTERNATIVES is None:
|
||||||
|
self._UPDATE_ALTERNATIVES = self.module.get_bin_path('update-alternatives', True)
|
||||||
|
return self._UPDATE_ALTERNATIVES
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
self.current_mode = None
|
||||||
|
self.current_path = None
|
||||||
|
self.current_link = None
|
||||||
|
self.current_alternatives = {}
|
||||||
|
|
||||||
|
# Run `update-alternatives --display <name>` to find existing alternatives
|
||||||
|
(rc, display_output, dummy) = self.module.run_command(
|
||||||
|
[self.UPDATE_ALTERNATIVES, '--display', self.name]
|
||||||
|
)
|
||||||
|
|
||||||
|
if rc != 0:
|
||||||
|
self.module.debug("No current alternative found. '%s' exited with %s" % (self.UPDATE_ALTERNATIVES, rc))
|
||||||
|
return
|
||||||
|
|
||||||
|
current_mode_regex = re.compile(r'\s-\s(?:status\sis\s)?(\w*)(?:\smode|.)$', re.MULTILINE)
|
||||||
|
current_path_regex = re.compile(r'^\s*link currently points to (.*)$', re.MULTILINE)
|
||||||
|
current_link_regex = re.compile(r'^\s*link \w+ is (.*)$', re.MULTILINE)
|
||||||
|
subcmd_path_link_regex = re.compile(r'^\s*slave (\S+) is (.*)$', re.MULTILINE)
|
||||||
|
|
||||||
|
alternative_regex = re.compile(r'^(\/.*)\s-\s(?:family\s\S+\s)?priority\s(\d+)((?:\s+slave.*)*)', re.MULTILINE)
|
||||||
|
subcmd_regex = re.compile(r'^\s+slave (.*): (.*)$', re.MULTILINE)
|
||||||
|
|
||||||
|
match = current_mode_regex.search(display_output)
|
||||||
|
if not match:
|
||||||
|
self.module.debug("No current mode found in output")
|
||||||
|
return
|
||||||
|
self.current_mode = match.group(1)
|
||||||
|
|
||||||
|
match = current_path_regex.search(display_output)
|
||||||
|
if not match:
|
||||||
|
self.module.debug("No current path found in output")
|
||||||
|
else:
|
||||||
|
self.current_path = match.group(1)
|
||||||
|
|
||||||
|
match = current_link_regex.search(display_output)
|
||||||
|
if not match:
|
||||||
|
self.module.debug("No current link found in output")
|
||||||
|
else:
|
||||||
|
self.current_link = match.group(1)
|
||||||
|
|
||||||
|
subcmd_path_map = dict(subcmd_path_link_regex.findall(display_output))
|
||||||
|
if not subcmd_path_map and self.subcommands:
|
||||||
|
subcmd_path_map = dict((s['name'], s['link']) for s in self.subcommands)
|
||||||
|
|
||||||
|
for path, prio, subcmd in alternative_regex.findall(display_output):
|
||||||
|
self.current_alternatives[path] = dict(
|
||||||
|
priority=int(prio),
|
||||||
|
subcommands=[dict(
|
||||||
|
name=name,
|
||||||
|
path=spath,
|
||||||
|
link=subcmd_path_map.get(name)
|
||||||
|
) for name, spath in subcmd_regex.findall(subcmd) if spath != '(null)']
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.module._diff:
|
||||||
|
if self.path in self.current_alternatives:
|
||||||
|
self.result['diff']['before'].update(dict(
|
||||||
|
state=AlternativeState.PRESENT,
|
||||||
|
path=self.path,
|
||||||
|
priority=self.current_alternatives[self.path].get('priority'),
|
||||||
|
link=self.current_link,
|
||||||
|
))
|
||||||
|
if self.current_alternatives[self.path].get('subcommands'):
|
||||||
|
self.result['diff']['before'].update(dict(
|
||||||
|
subcommands=self.current_alternatives[self.path].get('subcommands')
|
||||||
|
))
|
||||||
|
if self.current_mode == 'manual' and self.current_path != self.path:
|
||||||
|
self.result['diff']['before'].update(dict(
|
||||||
|
state=AlternativeState.SELECTED
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
self.result['diff']['before'].update(dict(
|
||||||
|
state=AlternativeState.ABSENT
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
@ -109,109 +379,16 @@ def main():
|
||||||
choices=AlternativeState.to_list(),
|
choices=AlternativeState.to_list(),
|
||||||
default=AlternativeState.SELECTED,
|
default=AlternativeState.SELECTED,
|
||||||
),
|
),
|
||||||
|
subcommands=dict(type='list', elements='dict', aliases=['slaves'], options=dict(
|
||||||
|
name=dict(type='str', required=True),
|
||||||
|
path=dict(type='path', required=True),
|
||||||
|
link=dict(type='path'),
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
params = module.params
|
AlternativesModule(module)
|
||||||
name = params['name']
|
|
||||||
path = params['path']
|
|
||||||
link = params['link']
|
|
||||||
priority = params['priority']
|
|
||||||
state = params['state']
|
|
||||||
|
|
||||||
UPDATE_ALTERNATIVES = module.get_bin_path('update-alternatives', True)
|
|
||||||
|
|
||||||
current_path = None
|
|
||||||
all_alternatives = []
|
|
||||||
|
|
||||||
# Run `update-alternatives --display <name>` to find existing alternatives
|
|
||||||
(rc, display_output, dummy) = module.run_command(
|
|
||||||
['env', 'LC_ALL=C', UPDATE_ALTERNATIVES, '--display', name]
|
|
||||||
)
|
|
||||||
|
|
||||||
if rc == 0:
|
|
||||||
# Alternatives already exist for this link group
|
|
||||||
# Parse the output to determine the current path of the symlink and
|
|
||||||
# available alternatives
|
|
||||||
current_path_regex = re.compile(r'^\s*link currently points to (.*)$',
|
|
||||||
re.MULTILINE)
|
|
||||||
alternative_regex = re.compile(r'^(\/.*)\s-\s(?:family\s\S+\s)?priority', re.MULTILINE)
|
|
||||||
|
|
||||||
match = current_path_regex.search(display_output)
|
|
||||||
if match:
|
|
||||||
current_path = match.group(1)
|
|
||||||
all_alternatives = alternative_regex.findall(display_output)
|
|
||||||
|
|
||||||
if not link:
|
|
||||||
# Read the current symlink target from `update-alternatives --query`
|
|
||||||
# in case we need to install the new alternative before setting it.
|
|
||||||
#
|
|
||||||
# This is only compatible on Debian-based systems, as the other
|
|
||||||
# alternatives don't have --query available
|
|
||||||
rc, query_output, dummy = module.run_command(
|
|
||||||
['env', 'LC_ALL=C', UPDATE_ALTERNATIVES, '--query', name]
|
|
||||||
)
|
|
||||||
if rc == 0:
|
|
||||||
for line in query_output.splitlines():
|
|
||||||
if line.startswith('Link:'):
|
|
||||||
link = line.split()[1]
|
|
||||||
break
|
|
||||||
|
|
||||||
changed = False
|
|
||||||
if current_path != path:
|
|
||||||
|
|
||||||
# Check mode: expect a change if this alternative is not already
|
|
||||||
# installed, or if it is to be set as the current selection.
|
|
||||||
if module.check_mode:
|
|
||||||
module.exit_json(
|
|
||||||
changed=(
|
|
||||||
path not in all_alternatives or
|
|
||||||
state == AlternativeState.SELECTED
|
|
||||||
),
|
|
||||||
current_path=current_path,
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# install the requested path if necessary
|
|
||||||
if path not in all_alternatives:
|
|
||||||
if not os.path.exists(path):
|
|
||||||
module.fail_json(msg="Specified path %s does not exist" % path)
|
|
||||||
if not link:
|
|
||||||
module.fail_json(msg="Needed to install the alternative, but unable to do so as we are missing the link")
|
|
||||||
|
|
||||||
module.run_command(
|
|
||||||
[UPDATE_ALTERNATIVES, '--install', link, name, path, str(priority)],
|
|
||||||
check_rc=True
|
|
||||||
)
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
# set the current selection to this path (if requested)
|
|
||||||
if state == AlternativeState.SELECTED:
|
|
||||||
module.run_command(
|
|
||||||
[UPDATE_ALTERNATIVES, '--set', name, path],
|
|
||||||
check_rc=True
|
|
||||||
)
|
|
||||||
changed = True
|
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
|
||||||
module.fail_json(msg=str(dir(cpe)))
|
|
||||||
elif current_path == path and state == AlternativeState.PRESENT:
|
|
||||||
# Case where alternative is currently selected, but state is set
|
|
||||||
# to 'present'. In this case, we set to auto mode.
|
|
||||||
if module.check_mode:
|
|
||||||
module.exit_json(changed=True, current_path=current_path)
|
|
||||||
|
|
||||||
changed = True
|
|
||||||
try:
|
|
||||||
module.run_command(
|
|
||||||
[UPDATE_ALTERNATIVES, '--auto', name],
|
|
||||||
check_rc=True,
|
|
||||||
)
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
|
||||||
module.fail_json(msg=str(dir(cpe)))
|
|
||||||
|
|
||||||
module.exit_json(changed=changed)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -49,6 +49,9 @@
|
||||||
# Test that path is checked: alternatives must fail when path is nonexistent
|
# Test that path is checked: alternatives must fail when path is nonexistent
|
||||||
- import_tasks: path_is_checked.yml
|
- import_tasks: path_is_checked.yml
|
||||||
|
|
||||||
|
# Test that subcommands commands work
|
||||||
|
- import_tasks: subcommands.yml
|
||||||
|
|
||||||
# Test operation of the 'state' parameter
|
# Test operation of the 'state' parameter
|
||||||
- block:
|
- block:
|
||||||
- include_tasks: remove_links.yml
|
- include_tasks: remove_links.yml
|
||||||
|
|
78
tests/integration/targets/alternatives/tasks/subcommands.yml
Normal file
78
tests/integration/targets/alternatives/tasks/subcommands.yml
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
- name: Try with subcommands
|
||||||
|
alternatives:
|
||||||
|
name: dummymain
|
||||||
|
path: '/usr/bin/dummy1'
|
||||||
|
link: '/usr/bin/dummymain'
|
||||||
|
subcommands:
|
||||||
|
- name: dummysubcmd
|
||||||
|
path: '/usr/bin/dummy2'
|
||||||
|
link: '/usr/bin/dummysubcmd'
|
||||||
|
register: alternative
|
||||||
|
|
||||||
|
- name: Check expected command was executed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- 'alternative is changed'
|
||||||
|
|
||||||
|
- name: Execute the current dummymain command
|
||||||
|
command: dummymain
|
||||||
|
register: cmd
|
||||||
|
|
||||||
|
- name: Ensure that the expected command was executed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmd.stdout == "dummy1"
|
||||||
|
|
||||||
|
- name: Execute the current dummysubcmd command
|
||||||
|
command: dummysubcmd
|
||||||
|
register: cmd
|
||||||
|
|
||||||
|
- name: Ensure that the expected command was executed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmd.stdout == "dummy2"
|
||||||
|
|
||||||
|
- name: Subcommands are not removed if not specified
|
||||||
|
alternatives:
|
||||||
|
name: dummymain
|
||||||
|
path: '/usr/bin/dummy1'
|
||||||
|
link: '/usr/bin/dummymain'
|
||||||
|
register: alternative
|
||||||
|
|
||||||
|
- name: Check expected command was executed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- 'alternative is not changed'
|
||||||
|
|
||||||
|
- name: Execute the current dummysubcmd command
|
||||||
|
command: dummysubcmd
|
||||||
|
register: cmd
|
||||||
|
|
||||||
|
- name: Ensure that the expected command was executed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmd.stdout == "dummy2"
|
||||||
|
|
||||||
|
- name: Subcommands are removed if set to an empty list
|
||||||
|
alternatives:
|
||||||
|
name: dummymain
|
||||||
|
path: '/usr/bin/dummy1'
|
||||||
|
link: '/usr/bin/dummymain'
|
||||||
|
subcommands: []
|
||||||
|
register: alternative
|
||||||
|
|
||||||
|
- name: Check expected command was executed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- 'alternative is changed'
|
||||||
|
|
||||||
|
- name: Execute the current dummysubcmd command
|
||||||
|
command: dummysubcmd
|
||||||
|
register: cmd
|
||||||
|
ignore_errors: True
|
||||||
|
|
||||||
|
- name: Ensure that the subcommand is gone
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmd.rc == 2
|
||||||
|
- '"No such file" in cmd.msg'
|
|
@ -48,6 +48,4 @@
|
||||||
when: ansible_os_family == 'RedHat' and not with_alternatives and item == 1
|
when: ansible_os_family == 'RedHat' and not with_alternatives and item == 1
|
||||||
|
|
||||||
- name: check that alternative has been updated
|
- name: check that alternative has been updated
|
||||||
command: "grep -Pzq '/bin/dummy{{ item }}\\n' '{{ alternatives_dir }}/dummy'"
|
command: "grep -Pzq '/bin/dummy{{ item }}\\n50' '{{ alternatives_dir }}/dummy'"
|
||||||
# priority doesn't seem updated
|
|
||||||
#command: "grep -Pzq '/bin/dummy{{ item }}\\n50' '{{ alternatives_dir }}/dummy'"
|
|
||||||
|
|
|
@ -21,3 +21,14 @@
|
||||||
|
|
||||||
- name: check that alternative has been updated
|
- name: check that alternative has been updated
|
||||||
command: "grep -Pzq '/bin/dummy{{ item }}\\n{{ 60 + item|int }}' '{{ alternatives_dir }}/dummy'"
|
command: "grep -Pzq '/bin/dummy{{ item }}\\n{{ 60 + item|int }}' '{{ alternatives_dir }}/dummy'"
|
||||||
|
|
||||||
|
- name: update dummy priority
|
||||||
|
alternatives:
|
||||||
|
name: dummy
|
||||||
|
path: '/usr/bin/dummy{{ item }}'
|
||||||
|
link: /usr/bin/dummy
|
||||||
|
priority: '{{ 70 + item|int }}'
|
||||||
|
register: alternative
|
||||||
|
|
||||||
|
- name: check that alternative priority has been updated
|
||||||
|
command: "grep -Pzq '/bin/dummy{{ item }}\\n{{ 70 + item|int }}' '{{ alternatives_dir }}/dummy'"
|
|
@ -49,6 +49,28 @@
|
||||||
- cmd.stdout == "dummy4"
|
- cmd.stdout == "dummy4"
|
||||||
|
|
||||||
# Set the currently selected alternative to state = 'present' (was previously
|
# Set the currently selected alternative to state = 'present' (was previously
|
||||||
|
# selected), and ensure that this results in the group not being set to 'auto'
|
||||||
|
# mode, and the alternative is still selected.
|
||||||
|
- name: Set current selected dummy to state = present
|
||||||
|
alternatives:
|
||||||
|
name: dummy
|
||||||
|
path: /usr/bin/dummy4
|
||||||
|
link: /usr/bin/dummy
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Ensure that the link group is in auto mode
|
||||||
|
shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^manual$"'
|
||||||
|
|
||||||
|
- name: Execute the current dummy command
|
||||||
|
shell: dummy
|
||||||
|
register: cmd
|
||||||
|
|
||||||
|
- name: Ensure that the expected command was executed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmd.stdout == "dummy4"
|
||||||
|
|
||||||
|
# Set the currently selected alternative to state = 'auto' (was previously
|
||||||
# selected), and ensure that this results in the group being set to 'auto'
|
# selected), and ensure that this results in the group being set to 'auto'
|
||||||
# mode, and the highest priority alternative is selected.
|
# mode, and the highest priority alternative is selected.
|
||||||
- name: Set current selected dummy to state = present
|
- name: Set current selected dummy to state = present
|
||||||
|
@ -56,7 +78,7 @@
|
||||||
name: dummy
|
name: dummy
|
||||||
path: /usr/bin/dummy4
|
path: /usr/bin/dummy4
|
||||||
link: /usr/bin/dummy
|
link: /usr/bin/dummy
|
||||||
state: present
|
state: auto
|
||||||
|
|
||||||
- name: Ensure that the link group is in auto mode
|
- name: Ensure that the link group is in auto mode
|
||||||
shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^auto$"'
|
shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^auto$"'
|
||||||
|
@ -69,3 +91,25 @@
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- cmd.stdout == "dummy2"
|
- cmd.stdout == "dummy2"
|
||||||
|
|
||||||
|
# Remove an alternative with state = 'absent' and make sure that
|
||||||
|
# this change results in the alternative being removed.
|
||||||
|
- name: Remove best dummy alternative with state = absent
|
||||||
|
alternatives:
|
||||||
|
name: dummy
|
||||||
|
path: /usr/bin/dummy2
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: Ensure that the link group is in auto mode
|
||||||
|
shell: 'grep "/usr/bin/dummy2" {{ alternatives_dir }}/dummy'
|
||||||
|
register: cmd
|
||||||
|
failed_when: cmd.rc == 0
|
||||||
|
|
||||||
|
- name: Execute the current dummy command
|
||||||
|
shell: dummy
|
||||||
|
register: cmd
|
||||||
|
|
||||||
|
- name: Ensure that the expected command was executed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmd.stdout == "dummy1"
|
||||||
|
|
Loading…
Reference in a new issue