Files
ansible-freeipa/plugins/modules/ipavault.py
Rafael Guterres Jeffman 88f84cefee Bypass Ansible filtering on data returned by the module.
Due to Ansible filtering out values in the output that might be match
values in sensible attributes that have `no_log` set, if a module need
to return data to the controller, it cannot rely on
`ansible_module.exit_json` if there is a chance that a partial match
may occur.

See: https://github.com/ansible/ansible/issues/71789

The change provided here uses the same implementation that is used on
Ansible's `AnsibleModule.exit_json`, without the data filtering layer,
so every attribute with be printed and, therefore, logged by Ansible.

This is needed for the Vault module, as we need to return values that
are explicit requested by the user and that might, at least partially,
match the values in attributes with `no_log` set.

Tests that reproduced the issue, and show it was fixed were provided
for all Vault types.
2020-11-13 14:14:07 -03:00

975 lines
35 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Authors:
# Rafael Guterres Jeffman <rjeffman@redhat.com>
#
# Copyright (C) 2019 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
"status": ["preview"],
}
DOCUMENTATION = """
---
module: ipavault
short description: Manage vaults and secret vaults.
description: Manage vaults and secret vaults. KRA service must be enabled.
options:
ipaadmin_principal:
description: The admin principal
default: admin
ipaadmin_password:
description: The admin password
required: false
name:
description: The vault name
required: true
aliases: ["cn"]
description:
description: The vault description
required: false
public_key:
description: Base64 encode public key.
required: false
type: string
aliases: ["ipavaultpublickey", "vault_public_key"]
public_key_file:
description: Path to file with public key.
required: false
type: string
aliases: ["vault_public_key_file"]
private_key:
description: Base64 encode private key.
required: false
type: string
aliases: ["ipavaultprivatekey", "vault_private_key"]
private_key_file:
description: Path to file with private key.
required: false
type: string
aliases: ["vault_private_key_file"]
password:
description: password to be used on symmetric vault.
required: false
type: string
aliases: ["ipavaultpassword", "vault_password", "old_password"]
password_file:
description: file with password to be used on symmetric vault.
required: false
type: string
aliases: ["vault_password_file", "old_password_file"]
new_password:
description: new password to be used on symmetric vault.
required: false
type: string
new_password_file:
description: file with new password to be used on symmetric vault.
required: false
type: string
salt:
description: Vault salt.
required: false
type: list
aliases: ["ipavaultsalt", "vault_salt"]
vault_type:
description: Vault types are based on security level.
required: true
default: symmetric
choices: ["standard", "symmetric", "asymmetric"]
aliases: ["ipavaulttype"]
service:
description: Any service can own one or more service vaults.
required: false
type: list
username:
description: Any user can own one or more user vaults.
required: false
type: string
aliases: ["user"]
shared:
description: Vault is shared.
required: false
type: boolean
users:
description: Users that are member of the vault.
required: false
type: list
groups:
description: Groups that are member of the vault.
required: false
type: list
owners:
description: Users that are owners of the vault.
required: false
type: list
ownergroups:
description: Groups that are owners of the vault.
required: false
type: list
ownerservices:
description: Services that are owners of the vault.
required: false
type: list
services:
description: Services that are member of the container.
required: false
type: list
data:
description: Data to be stored in the vault.
required: false
type: string
aliases: ["ipavaultdata", "vault_data"]
in:
description: Path to file with data to be stored in the vault.
required: false
type: string
aliases: ["datafile_in"]
out:
description: Path to file to store data retrieved from the vault.
required: false
type: string
aliases: ["datafile_out"]
action:
description: Work on vault or member level.
default: vault
choices: ["vault", "member"]
state:
description: State to ensure
default: present
choices: ["present", "absent", "retrieved"]
author:
- Rafael Jeffman
"""
EXAMPLES = """
# Ensure vault symvault is present
- ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
username: admin
vault_type: symmetric
password: SomeVAULTpassword
salt: MTIzNDU2Nzg5MAo=
# Ensure group ipausers is a vault member.
- ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
username: admin
groups: ipausers
action: member
# Ensure group ipausers is not a vault member.
- ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
username: admin
groups: ipausers
action: member
state: absent
# Ensure vault users are present.
- ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
username: admin
users:
- user01
- user02
action: member
# Ensure vault users are absent.
- ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
username: admin
users:
- user01
- user02
action: member
status: absent
# Ensure user owns vault.
- ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
username: admin
action: member
owners: user01
# Ensure user does not own vault.
- ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
username: admin
owners: user01
action: member
status: absent
# Ensure data is archived to a symmetric vault
- ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
username: admin
password: SomeVAULTpassword
data: >
Data archived.
More data archived.
action: member
# Retrieve data archived from a symmetric vault
- ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
username: admin
password: SomeVAULTpassword
state: retrieved
register: result
- debug:
msg: "{{ result.vault.data }}"
# Change password of a symmetric vault
- ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
username: admin
old_password: SomeVAULTpassword
new_password: SomeNEWpassword
# Ensure vault symvault is absent
- ipavault:
ipaadmin_password: SomeADMINpassword
name: symvault
user: admin
state: absent
# Ensure asymmetric vault is present.
- ipavault:
ipaadmin_password: SomeADMINpassword
name: asymvault
username: user01
description: An asymmetric vault
vault_type: asymmetric
public_key: |
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTR
HTkFEQ0JpUUtCZ1FDdGFudjRkK3ptSTZ0T3ova1RXdGowY3AxRAowUENoYy8vR0pJMTUzTi
9CN3UrN0h3SXlRVlZoNUlXZG1UcCtkWXYzd09yeVpPbzYvbHN5eFJaZ2pZRDRwQ3VGCjlxM
295VTFEMnFOZERYeGtSaFFETXBiUEVSWWlHbE1jbzdhN0hIVDk1bGNQbmhObVFkb3VGdHlV
bFBUVS96V1kKZldYWTBOeU1UbUtoeFRseUV3SURBUUFCCi0tLS0tRU5EIFBVQkxJQyBLRVk
tLS0tLQo=
# Ensure data is archived in an asymmetric vault
- ipavault:
ipaadmin_password: SomeADMINpassword
name: asymvault
username: admin
data: >
Data archived.
More data archived.
action: member
# Retrive data archived in an asymmetric vault, using a private key file.
- ipavault:
ipaadmin_password: SomeADMINpassword
name: asymvault
username: admin
private_key_file: private.pem
state: retrieved
# Ensure asymmetric vault is absent.
- ipavault:
ipaadmin_password: SomeADMINpassword
name: asymvault
username: user01
vault_type: asymmetric
state: absent
"""
RETURN = """
vault:
description: Vault dict with archived data.
returned: If state is `retrieved`.
type: dict
options:
data:
description: The vault data.
returned: always
type: string
"""
import os
from base64 import b64decode
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
temp_kdestroy, valid_creds, api_connect, api_command, \
gen_add_del_lists, compare_args_ipa, module_params_get, exit_raw_json
from ipalib.errors import EmptyModlist
def find_vault(module, name, username, service, shared):
_args = {
"all": True,
"cn": name,
}
if username is not None:
_args['username'] = username
elif service is not None:
_args['service'] = service
else:
_args['shared'] = shared
_result = api_command(module, "vault_find", name, _args)
if len(_result["result"]) > 1:
module.fail_json(
msg="There is more than one vault '%s'" % (name))
if len(_result["result"]) == 1:
return _result["result"][0]
return None
def gen_args(description, username, service, shared, vault_type, salt,
password, password_file, public_key, public_key_file, vault_data,
datafile_in, datafile_out):
_args = {}
if description is not None:
_args['description'] = description
if username is not None:
_args['username'] = username
if service is not None:
_args['service'] = service
if shared is not None:
_args['shared'] = shared
if vault_type is not None:
_args['ipavaulttype'] = vault_type
if salt is not None:
_args['ipavaultsalt'] = salt
if public_key is not None:
_args['ipavaultpublickey'] = b64decode(public_key.encode('utf-8'))
if public_key_file is not None:
with open(public_key_file, 'r') as keyfile:
keydata = keyfile.read()
_args['ipavaultpublickey'] = keydata.strip().encode('utf-8')
return _args
def gen_member_args(args, users, groups, services):
_args = args.copy()
for arg in ['ipavaulttype', 'description', 'ipavaultpublickey',
'ipavaultsalt']:
if arg in _args:
del _args[arg]
if any([users, groups, services]):
if users is not None:
_args['user'] = users
if groups is not None:
_args['group'] = groups
if services is not None:
_args['services'] = services
return _args
return None
def data_storage_args(args, data, password, password_file, private_key,
private_key_file, datafile_in, datafile_out):
_args = {}
if 'username' in args:
_args['username'] = args['username']
if 'service' in args:
_args['service'] = args['service']
if 'shared' in args:
_args['shared'] = args['shared']
if password is not None:
_args['password'] = password
if password_file is not None:
_args['password_file'] = password_file
if private_key is not None:
_args['private_key'] = private_key
if private_key_file is not None:
_args['private_key_file'] = private_key_file
if datafile_in is not None:
_args['in'] = datafile_in
else:
if data is None:
_args['data'] = b''
else:
_args['data'] = data.encode('utf-8')
if datafile_out is not None:
_args['out'] = datafile_out
if private_key_file is not None:
_args['private_key_file'] = private_key_file
return _args
def check_parameters(module, state, action, description, username, service,
shared, users, groups, services, owners, ownergroups,
ownerservices, vault_type, salt, password, password_file,
public_key, public_key_file, private_key,
private_key_file, vault_data, datafile_in, datafile_out,
new_password, new_password_file):
invalid = []
if state == "present":
invalid = ['private_key', 'private_key_file', 'datafile_out']
if all([password, password_file]) \
or all([new_password, new_password_file]):
module.fail_json(msg="Password specified multiple times.")
if any([new_password, new_password_file]) \
and not any([password, password_file]):
module.fail_json(
msg="Either `password` or `password_file` must be provided to "
"change symmetric vault password.")
if action == "member":
invalid.extend(['description'])
elif state == "absent":
invalid = ['description', 'salt', 'vault_type', 'private_key',
'private_key_file', 'datafile_in', 'datafile_out',
'vault_data', 'new_password', 'new_password_file']
if action == "vault":
invalid.extend(['users', 'groups', 'services', 'owners',
'ownergroups', 'ownerservices', 'password',
'password_file', 'public_key', 'public_key_file'])
elif state == "retrieved":
invalid = ['description', 'salt', 'datafile_in', 'users', 'groups',
'owners', 'ownergroups', 'public_key', 'public_key_file',
'vault_data', 'new_password', 'new_password_file']
if action == 'member':
module.fail_json(
msg="State `retrieved` do not support action `member`.")
for arg in invalid:
if vars()[arg] is not None:
module.fail_json(
msg="Argument '%s' can not be used with state '%s', "
"action '%s'" % (arg, state, action))
for arg in invalid:
if vars()[arg] is not None:
module.fail_json(
msg="Argument '%s' can not be used with state '%s', "
"action '%s'" % (arg, state, action))
def check_encryption_params(module, state, action, vault_type, salt,
password, password_file, public_key,
public_key_file, private_key, private_key_file,
vault_data, datafile_in, datafile_out,
new_password, new_password_file, res_find):
vault_type_invalid = []
if vault_type is None and res_find is not None:
vault_type = res_find['ipavaulttype']
if isinstance(vault_type, (tuple, list)):
vault_type = vault_type[0]
if vault_type == "standard":
vault_type_invalid = ['public_key', 'public_key_file', 'password',
'password_file', 'salt', 'new_password',
'new_password_file']
if vault_type is None or vault_type == "symmetric":
vault_type_invalid = ['public_key', 'public_key_file',
'private_key', 'private_key_file']
if password is None and password_file is None and action != 'member':
module.fail_json(
msg="Symmetric vault requires password or password_file "
"to store data or change `salt`.")
if any([new_password, new_password_file]) and res_find is None:
module.fail_json(
msg="Cannot modify password of inexistent vault.")
if (
salt is not None
and not(
any([password, password_file])
and any([new_password, new_password_file])
)
):
module.fail_json(
msg="Vault `salt` can only change when changing the password.")
if vault_type == "asymmetric":
vault_type_invalid = [
'password', 'password_file', 'new_password', 'new_password_file'
]
if not any([public_key, public_key_file]) and res_find is None:
module.fail_json(
msg="Assymmetric vault requires public_key "
"or public_key_file to store data.")
for param in vault_type_invalid:
if vars()[param] is not None:
module.fail_json(
msg="Argument '%s' cannot be used with vault type '%s'" %
(param, vault_type or 'symmetric'))
def change_password(module, res_find, password, password_file, new_password,
new_password_file):
"""
Change the password of a symmetric vault.
To change the password of a vault, it is needed to retrieve the stored
data with the current password, and store the data again, with the new
password, forcing it to override the old one.
"""
# verify parameters.
if not any([new_password, new_password_file]):
return []
if res_find["ipavaulttype"][0] != "symmetric":
module.fail_json(msg="Cannot change password of `%s` vault."
% res_find["ipavaulttype"])
# prepare arguments to retrieve data.
name = res_find["cn"][0]
args = {}
if password:
args["password"] = password
if password_file:
args["password_file"] = password_file
# retrieve current stored data
result = api_command(module, 'vault_retrieve', name, args)
# modify arguments to store data with new password.
args = {"override_password": True, "data": result['result']['data']}
if new_password:
args["password"] = new_password
if new_password_file:
args["password_file"] = new_password_file
# return the command to store data with the new password.
return [(name, "vault_archive", args)]
def main():
ansible_module = AnsibleModule(
argument_spec=dict(
# generalgroups
ipaadmin_principal=dict(type="str", default="admin"),
ipaadmin_password=dict(type="str", required=False, no_log=True),
name=dict(type="list", aliases=["cn"], default=None,
required=True),
description=dict(required=False, type="str", default=None),
vault_type=dict(type="str", aliases=["ipavaulttype"],
default=None, required=False,
choices=["standard", "symmetric", "asymmetric"]),
vault_public_key=dict(type="str", required=False, default=None,
aliases=['ipavaultpublickey', 'public_key']),
vault_public_key_file=dict(type="str", required=False,
default=None,
aliases=['public_key_file']),
vault_private_key=dict(
type="str", required=False, default=None, no_log=True,
aliases=['ipavaultprivatekey', 'private_key']),
vault_private_key_file=dict(type="str", required=False,
default=None,
aliases=['private_key_file']),
vault_salt=dict(type="str", required=False, default=None,
aliases=['ipavaultsalt', 'salt']),
username=dict(type="str", required=False, default=None,
aliases=['user']),
service=dict(type="str", required=False, default=None),
shared=dict(type="bool", required=False, default=None),
users=dict(required=False, type='list', default=None),
groups=dict(required=False, type='list', default=None),
services=dict(required=False, type='list', default=None),
owners=dict(required=False, type='list', default=None,
aliases=['ownerusers']),
ownergroups=dict(required=False, type='list', default=None),
ownerservices=dict(required=False, type='list', default=None),
vault_data=dict(type="str", required=False, default=None,
no_log=True, aliases=['ipavaultdata', 'data']),
datafile_in=dict(type="str", required=False, default=None,
aliases=['in']),
datafile_out=dict(type="str", required=False, default=None,
aliases=['out']),
vault_password=dict(type="str", required=False, default=None,
no_log=True,
aliases=['ipavaultpassword', 'password',
"old_password"]),
vault_password_file=dict(type="str", required=False, default=None,
no_log=False,
aliases=[
'password_file', "old_password_file"
]),
new_password=dict(type="str", required=False, default=None,
no_log=True),
new_password_file=dict(type="str", required=False, default=None,
no_log=False),
# state
action=dict(type="str", default="vault",
choices=["vault", "data", "member"]),
state=dict(type="str", default="present",
choices=["present", "absent", "retrieved"]),
),
supports_check_mode=True,
mutually_exclusive=[['username', 'service', 'shared'],
['datafile_in', 'vault_data'],
['new_password', 'new_password_file'],
['vault_password', 'vault_password_file'],
['vault_public_key', 'vault_public_key_file']],
)
ansible_module._ansible_debug = True
# general
ipaadmin_principal = module_params_get(ansible_module,
"ipaadmin_principal")
ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
names = module_params_get(ansible_module, "name")
# present
description = module_params_get(ansible_module, "description")
username = module_params_get(ansible_module, "username")
service = module_params_get(ansible_module, "service")
shared = module_params_get(ansible_module, "shared")
users = module_params_get(ansible_module, "users")
groups = module_params_get(ansible_module, "groups")
services = module_params_get(ansible_module, "services")
owners = module_params_get(ansible_module, "owners")
ownergroups = module_params_get(ansible_module, "ownergroups")
ownerservices = module_params_get(ansible_module, "ownerservices")
vault_type = module_params_get(ansible_module, "vault_type")
salt = module_params_get(ansible_module, "vault_salt")
password = module_params_get(ansible_module, "vault_password")
password_file = module_params_get(ansible_module, "vault_password_file")
new_password = module_params_get(ansible_module, "new_password")
new_password_file = module_params_get(ansible_module, "new_password_file")
public_key = module_params_get(ansible_module, "vault_public_key")
public_key_file = module_params_get(ansible_module,
"vault_public_key_file")
private_key = module_params_get(ansible_module, "vault_private_key")
private_key_file = module_params_get(ansible_module,
"vault_private_key_file")
vault_data = module_params_get(ansible_module, "vault_data")
datafile_in = module_params_get(ansible_module, "datafile_in")
datafile_out = module_params_get(ansible_module, "datafile_out")
action = module_params_get(ansible_module, "action")
state = module_params_get(ansible_module, "state")
# Check parameters
if state == "present":
if len(names) != 1:
ansible_module.fail_json(
msg="Only one vault can be added at a time.")
elif state == "absent":
if len(names) < 1:
ansible_module.fail_json(msg="No name given.")
elif state == "retrieved":
if len(names) != 1:
ansible_module.fail_json(
msg="Only one vault can be retrieved at a time.")
else:
ansible_module.fail_json(msg="Invalid state '%s'" % state)
check_parameters(ansible_module, state, action, description, username,
service, shared, users, groups, services, owners,
ownergroups, ownerservices, vault_type, salt, password,
password_file, public_key, public_key_file, private_key,
private_key_file, vault_data, datafile_in, datafile_out,
new_password, new_password_file)
# Init
changed = False
exit_args = {}
ccache_dir = None
ccache_name = None
try:
if not valid_creds(ansible_module, ipaadmin_principal):
ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
ipaadmin_password)
# Need to set krb5 ccache name, due to context='ansible-freeipa'
if ccache_name is not None:
os.environ["KRB5CCNAME"] = ccache_name
api_connect(context='ansible-freeipa')
commands = []
for name in names:
# Make sure vault exists
res_find = find_vault(
ansible_module, name, username, service, shared)
# Generate args
args = gen_args(description, username, service, shared, vault_type,
salt, password, password_file, public_key,
public_key_file, vault_data, datafile_in,
datafile_out)
pwdargs = None
# Set default vault_type if needed.
if vault_type is None and vault_data is not None:
if res_find is not None:
res_vault_type = res_find.get('ipavaulttype')[0]
args['ipavaulttype'] = vault_type = res_vault_type
else:
args['ipavaulttype'] = vault_type = u"symmetric"
# Create command
if state == "present":
# verify data encription args
check_encryption_params(
ansible_module, state, action, vault_type, salt, password,
password_file, public_key, public_key_file, private_key,
private_key_file, vault_data, datafile_in, datafile_out,
new_password, new_password_file, res_find)
# Found the vault
if action == "vault":
if res_find is not None:
# For all settings is args, check if there are
# different settings in the find result.
# If yes: modify
if not compare_args_ipa(ansible_module, args,
res_find):
commands.append([name, "vault_mod_internal", args])
else:
if vault_type == 'symmetric' \
and 'ipavaultsalt' not in args:
args['ipavaultsalt'] = os.urandom(32)
commands.append([name, "vault_add_internal", args])
if vault_type != 'standard' and vault_data is None:
vault_data = ''
# Set res_find to empty dict for next steps
res_find = {}
# Generate adittion and removal lists
user_add, user_del = \
gen_add_del_lists(users,
res_find.get('member_user', []))
group_add, group_del = \
gen_add_del_lists(groups,
res_find.get('member_group', []))
service_add, service_del = \
gen_add_del_lists(services,
res_find.get('member_service', []))
owner_add, owner_del = \
gen_add_del_lists(owners,
res_find.get('owner_user', []))
ownergroups_add, ownergroups_del = \
gen_add_del_lists(ownergroups,
res_find.get('owner_group', []))
ownerservice_add, ownerservice_del = \
gen_add_del_lists(ownerservices,
res_find.get('owner_service', []))
# Add users and groups
user_add_args = gen_member_args(args, user_add,
group_add, service_add)
if user_add_args is not None:
commands.append(
[name, 'vault_add_member', user_add_args])
# Remove users and groups
user_del_args = gen_member_args(args, user_del,
group_del, service_del)
if user_del_args is not None:
commands.append(
[name, 'vault_remove_member', user_del_args])
# Add owner users and groups
owner_add_args = gen_member_args(
args, owner_add, ownergroups_add, ownerservice_add)
if owner_add_args is not None:
commands.append(
[name, 'vault_add_owner', owner_add_args])
# Remove owner users and groups
owner_del_args = gen_member_args(
args, owner_del, ownergroups_del, ownerservice_del)
if owner_del_args is not None:
commands.append(
[name, 'vault_remove_owner', owner_del_args])
elif action in "member":
# Add users and groups
if any([users, groups, services]):
user_args = gen_member_args(args, users, groups,
services)
commands.append([name, 'vault_add_member', user_args])
if any([owners, ownergroups, ownerservices]):
owner_args = gen_member_args(args, owners, ownergroups,
ownerservices)
commands.append([name, 'vault_add_owner', owner_args])
pwdargs = data_storage_args(
args, vault_data, password, password_file, private_key,
private_key_file, datafile_in, datafile_out)
if any([vault_data, datafile_in]):
commands.append([name, "vault_archive", pwdargs])
cmds = change_password(
ansible_module, res_find, password, password_file,
new_password, new_password_file)
commands.extend(cmds)
elif state == "retrieved":
if res_find is None:
ansible_module.fail_json(
msg="Vault `%s` not found to retrieve data." % name)
# verify data encription args
check_encryption_params(
ansible_module, state, action, vault_type, salt, password,
password_file, public_key, public_key_file, private_key,
private_key_file, vault_data, datafile_in, datafile_out,
new_password, new_password_file, res_find)
pwdargs = data_storage_args(
args, vault_data, password, password_file, private_key,
private_key_file, datafile_in, datafile_out)
if 'data' in pwdargs:
del pwdargs['data']
commands.append([name, "vault_retrieve", pwdargs])
elif state == "absent":
if 'ipavaulttype' in args:
del args['ipavaulttype']
if action == "vault":
if res_find is not None:
commands.append([name, "vault_del", args])
elif action == "member":
# remove users and groups
if any([users, groups, services]):
user_args = gen_member_args(
args, users, groups, services)
commands.append(
[name, 'vault_remove_member', user_args])
if any([owners, ownergroups, ownerservices]):
owner_args = gen_member_args(
args, owners, ownergroups, ownerservices)
commands.append(
[name, 'vault_remove_owner', owner_args])
else:
ansible_module.fail_json(
msg="Invalid action '%s' for state '%s'" %
(action, state))
else:
ansible_module.fail_json(msg="Unknown state '%s'" % state)
# Execute commands
errors = []
for name, command, args in commands:
try:
result = api_command(ansible_module, command, name, args)
if command == 'vault_archive':
changed = 'Archived data into' in result['summary']
elif command == 'vault_retrieve':
if 'result' not in result:
raise Exception("No result obtained.")
if "data" in result["result"]:
data_return = exit_args.setdefault("vault", {})
data_return["data"] = result["result"]["data"]
else:
if not datafile_out:
raise Exception("No data retrieved.")
changed = False
else:
if "completed" in result:
if result["completed"] > 0:
changed = True
else:
changed = True
except EmptyModlist:
result = {}
except Exception as exception:
ansible_module.fail_json(
msg="%s: %s: %s" % (command, name, str(exception)))
# Get all errors
# All "already a member" and "not a member" failures in the
# result are ignored. All others are reported.
if "failed" in result and len(result["failed"]) > 0:
for item in result["failed"]:
failed_item = result["failed"][item]
for member_type in failed_item:
for member, failure in failed_item[member_type]:
if "already a member" in failure \
or "not a member" in failure:
continue
errors.append("%s: %s %s: %s" % (
command, member_type, member, failure))
if len(errors) > 0:
ansible_module.fail_json(msg=", ".join(errors))
except Exception as exception:
ansible_module.fail_json(msg=str(exception))
finally:
temp_kdestroy(ccache_dir, ccache_name)
# Done
# exit_raw_json is a replacement for ansible_module.exit_json that
# does not mask the output.
exit_raw_json(ansible_module, changed=changed, **exit_args)
if __name__ == "__main__":
main()