Blame roles/ipaclient/library/ipaclient_get_otp.py

Packit Service ee01e6
#!/usr/bin/python
Packit Service 0a38ef
# -*- coding: utf-8 -*-
Packit Service 0a38ef
Packit Service 0a38ef
# Authors:
Packit Service 0a38ef
#   Florence Blanc-Renaud <frenaud@redhat.com>
Packit Service 0a38ef
#
Packit Service 0a38ef
# Copyright (C) 2017  Red Hat
Packit Service 0a38ef
# see file 'COPYING' for use and warranty information
Packit Service 0a38ef
#
Packit Service 0a38ef
# This program is free software; you can redistribute it and/or modify
Packit Service 0a38ef
# it under the terms of the GNU General Public License as published by
Packit Service 0a38ef
# the Free Software Foundation, either version 3 of the License, or
Packit Service 0a38ef
# (at your option) any later version.
Packit Service 0a38ef
#
Packit Service 0a38ef
# This program is distributed in the hope that it will be useful,
Packit Service 0a38ef
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 0a38ef
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 0a38ef
# GNU General Public License for more details.
Packit Service 0a38ef
#
Packit Service 0a38ef
# You should have received a copy of the GNU General Public License
Packit Service 0a38ef
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit Service 0a38ef
Packit Service 0a38ef
ANSIBLE_METADATA = {'metadata_version': '1.0',
Packit Service 0a38ef
                    'status': ['preview'],
Packit Service 0a38ef
                    'supported_by': 'community'}
Packit Service 0a38ef
Packit Service 0a38ef
DOCUMENTATION = '''
Packit Service 0a38ef
---
Packit Service 0a38ef
module: ipaclient_get_otp
Packit Service 0a38ef
short description: Manage IPA hosts
Packit Service 0a38ef
description:
Packit Service 0a38ef
  Manage hosts in a IPA domain.
Packit Service 0a38ef
  The operation needs to be authenticated with Kerberos either by providing
Packit Service 0a38ef
  a password or a keytab corresponding to a principal allowed to perform
Packit Service 0a38ef
  host operations.
Packit Service 0a38ef
options:
Packit Service 0a38ef
  principal:
Packit Service 0a38ef
    description:
Packit Service 0a38ef
      User Principal allowed to promote replicas and join IPA realm
Packit Service 0a38ef
    required: yes
Packit Service 0a38ef
  ccache:
Packit Service 0a38ef
    description: The local ccache
Packit Service 0a38ef
    required: yes
Packit Service 0a38ef
  fqdn:
Packit Service 0a38ef
    description:
Packit Service 0a38ef
      The fully-qualified hostname of the host to add/modify/remove
Packit Service 0a38ef
    required: no
Packit Service 0a38ef
  certificates:
Packit Service 0a38ef
    description: A list of host certificates
Packit Service 0a38ef
    required: yes
Packit Service 0a38ef
  sshpubkey:
Packit Service 0a38ef
    description: The SSH public key for the host
Packit Service 0a38ef
    required: yes
Packit Service 0a38ef
  ipaddress:
Packit Service 0a38ef
    description: The IP address for the host
Packit Service 0a38ef
    required: yes
Packit Service 0a38ef
  random:
Packit Service 0a38ef
    description: Generate a random password to be used in bulk enrollment
Packit Service 0a38ef
    required: yes
Packit Service 0a38ef
  state:
Packit Service 0a38ef
    description: The desired host state
Packit Service 0a38ef
    required: yes
Packit Service 0a38ef
author:
Packit Service 0a38ef
    - "Florence Blanc-Renaud"
Packit Service 0a38ef
'''
Packit Service 0a38ef
Packit Service 0a38ef
EXAMPLES = '''
Packit Service 0a38ef
# Example from Ansible Playbooks
Packit Service 0a38ef
# Add a new host with a random OTP, authenticate using principal/password
Packit Service 0a38ef
- ipaclient_get_otp:
Packit Service 0a38ef
    principal: admin
Packit Service 0a38ef
    password: MySecretPassword
Packit Service 0a38ef
    fqdn: ipaclient.ipa.domain.com
Packit Service 0a38ef
    ipaddress: 192.168.100.23
Packit Service 0a38ef
    random: True
Packit Service 0a38ef
  register: result_ipaclient_get_otp
Packit Service 0a38ef
'''
Packit Service 0a38ef
Packit Service 0a38ef
RETURN = '''
Packit Service 0a38ef
host:
Packit Service 0a38ef
  description: the host structure as returned from IPA API
Packit Service 0a38ef
  returned: always
Packit Service 0a38ef
  type: complex
Packit Service 0a38ef
  contains:
Packit Service 0a38ef
    dn:
Packit Service 0a38ef
      description: the DN of the host entry
Packit Service 0a38ef
      type: string
Packit Service 0a38ef
      returned: always
Packit Service 0a38ef
    fqdn:
Packit Service 0a38ef
      description: the fully qualified host name
Packit Service 0a38ef
      type: string
Packit Service 0a38ef
      returned: always
Packit Service 0a38ef
    has_keytab:
Packit Service 0a38ef
      description: whether the host entry contains a keytab
Packit Service 0a38ef
      type: bool
Packit Service 0a38ef
      returned: always
Packit Service 0a38ef
    has_password:
Packit Service 0a38ef
      description: whether the host entry contains a password
Packit Service 0a38ef
      type: bool
Packit Service 0a38ef
      returned: always
Packit Service 0a38ef
    managedby_host:
Packit Service 0a38ef
      description: the list of hosts managing the host
Packit Service 0a38ef
      type: list
Packit Service 0a38ef
      returned: always
Packit Service 0a38ef
    randompassword:
Packit Service 0a38ef
      description: the OneTimePassword generated for this host
Packit Service 0a38ef
      type: string
Packit Service 0a38ef
      returned: changed
Packit Service 0a38ef
    certificates:
Packit Service 0a38ef
      description: the list of host certificates
Packit Service 0a38ef
      type: list
Packit Service 0a38ef
      returned: when present
Packit Service 0a38ef
    sshpubkey:
Packit Service 0a38ef
      description: the SSH public key for the host
Packit Service 0a38ef
      type: string
Packit Service 0a38ef
      returned: when present
Packit Service 0a38ef
    ipaddress:
Packit Service 0a38ef
      description: the IP address for the host
Packit Service 0a38ef
      type: string
Packit Service 0a38ef
      returned: when present
Packit Service 0a38ef
'''
Packit Service 0a38ef
Packit Service 0a38ef
import os
Packit Service 0a38ef
import six
Packit Service 0a38ef
Packit Service 0a38ef
from ansible.module_utils.basic import AnsibleModule
Packit Service 0a38ef
Packit Service 0a38ef
from ipalib import api, errors
Packit Service 0a38ef
from ipaplatform.paths import paths
Packit Service 0a38ef
from ipapython.ipautil import run
Packit Service 0a38ef
Packit Service 0a38ef
if six.PY3:
Packit Service 0a38ef
    unicode = str
Packit Service 0a38ef
Packit Service 0a38ef
Packit Service 0a38ef
def get_host_diff(ipa_host, module_host):
Packit Service 0a38ef
    """
Packit Service 0a38ef
    Build a dict with the differences from two host dicts.
Packit Service 0a38ef
Packit Service 0a38ef
    :param ipa_host: the host structure seen from IPA
Packit Service 0a38ef
    :param module_host: the target host structure seen from the module params
Packit Service 0a38ef
Packit Service 0a38ef
    :return: a dict representing the host attributes to apply
Packit Service 0a38ef
    """
Packit Service 0a38ef
    non_updateable_keys = ['ip_address']
Packit Service 0a38ef
    data = dict()
Packit Service 0a38ef
    for key in non_updateable_keys:
Packit Service 0a38ef
        if key in module_host:
Packit Service 0a38ef
            del module_host[key]
Packit Service 0a38ef
Packit Service 0a38ef
    for key in module_host.keys():
Packit Service 0a38ef
        ipa_value = ipa_host.get(key, None)
Packit Service 0a38ef
        module_value = module_host.get(key, None)
Packit Service 0a38ef
        if isinstance(ipa_value, list) and not isinstance(module_value, list):
Packit Service 0a38ef
            module_value = [module_value]
Packit Service 0a38ef
        if isinstance(ipa_value, list) and isinstance(module_value, list):
Packit Service 0a38ef
            ipa_value = sorted(ipa_value)
Packit Service 0a38ef
            module_value = sorted(module_value)
Packit Service 0a38ef
        if ipa_value != module_value:
Packit Service 0a38ef
            data[key] = unicode(module_value)
Packit Service 0a38ef
    return data
Packit Service 0a38ef
Packit Service 0a38ef
Packit Service 0a38ef
def get_module_host(module):
Packit Service 0a38ef
    """
Packit Service 0a38ef
    Create a structure representing the host information.
Packit Service 0a38ef
Packit Service 0a38ef
    Reads the module parameters and builds the host structure as expected from
Packit Service 0a38ef
    the module
Packit Service 0a38ef
    :param module: the ansible module
Packit Service 0a38ef
    :returns: a dict representing the host attributes
Packit Service 0a38ef
    """
Packit Service 0a38ef
    data = dict()
Packit Service 0a38ef
    certificates = module.params.get('certificates')
Packit Service 0a38ef
    if certificates:
Packit Service 0a38ef
        data['usercertificate'] = certificates
Packit Service 0a38ef
    sshpubkey = module.params.get('sshpubkey')
Packit Service 0a38ef
    if sshpubkey:
Packit Service 0a38ef
        data['ipasshpubkey'] = unicode(sshpubkey)
Packit Service 0a38ef
    ipaddress = module.params.get('ipaddress')
Packit Service 0a38ef
    if ipaddress:
Packit Service 0a38ef
        data['ip_address'] = unicode(ipaddress)
Packit Service 0a38ef
    random = module.params.get('random')
Packit Service 0a38ef
    if random:
Packit Service 0a38ef
        data['random'] = random
Packit Service 0a38ef
    return data
Packit Service 0a38ef
Packit Service 0a38ef
Packit Service 0a38ef
def ensure_host_present(module, api, ipahost):
Packit Service 0a38ef
    """
Packit Service 0a38ef
    Ensure host exists in IPA and has the same attributes.
Packit Service 0a38ef
Packit Service 0a38ef
    :param module: the ansible module
Packit Service 0a38ef
    :param api: IPA api handle
Packit Service 0a38ef
    :param ipahost: the host information present in IPA, can be none if the
Packit Service 0a38ef
                    host does not exist
Packit Service 0a38ef
    """
Packit Service 0a38ef
    fqdn = unicode(module.params.get('fqdn'))
Packit Service 0a38ef
    if ipahost:
Packit Service 0a38ef
        # Host already present, need to compare the attributes
Packit Service 0a38ef
        module_host = get_module_host(module)
Packit Service 0a38ef
        diffs = get_host_diff(ipahost, module_host)
Packit Service 0a38ef
Packit Service 0a38ef
        if not diffs:
Packit Service 0a38ef
            # Same attributes, success
Packit Service 0a38ef
            module.exit_json(changed=False, host=ipahost)
Packit Service 0a38ef
Packit Service 0a38ef
        # Need to modify the host - only if not in check_mode
Packit Service 0a38ef
        if module.check_mode:
Packit Service 0a38ef
            module.exit_json(changed=True)
Packit Service 0a38ef
Packit Service 0a38ef
        # If we want to create a random password, and the host
Packit Service 0a38ef
        # already has Keytab: true, then we need first to run
Packit Service 0a38ef
        # ipa host-disable in order to remove OTP and keytab
Packit Service 0a38ef
        if module.params.get('random') and ipahost['has_keytab'] is True:
Packit Service 0a38ef
            api.Command.host_disable(fqdn)
Packit Service 0a38ef
Packit Service 0a38ef
        result = api.Command.host_mod(fqdn, **diffs)
Packit Service 0a38ef
        # Save random password as it is not displayed by host-show
Packit Service 0a38ef
        if module.params.get('random'):
Packit Service 0a38ef
            randompassword = result['result']['randompassword']
Packit Service 0a38ef
        result = api.Command.host_show(fqdn)
Packit Service 0a38ef
        if module.params.get('random'):
Packit Service 0a38ef
            result['result']['randompassword'] = randompassword
Packit Service 0a38ef
        module.exit_json(changed=True, host=result['result'])
Packit Service 0a38ef
Packit Service 0a38ef
    if not ipahost:
Packit Service 0a38ef
        # Need to add the user, only if not in check_mode
Packit Service 0a38ef
        if module.check_mode:
Packit Service 0a38ef
            module.exit_json(changed=True)
Packit Service 0a38ef
Packit Service 0a38ef
        # Must add the user
Packit Service 0a38ef
        module_host = get_module_host(module)
Packit Service 0a38ef
        # force creation of host even if there is no DNS record
Packit Service 0a38ef
        module_host["force"] = True
Packit Service 0a38ef
        result = api.Command.host_add(fqdn, **module_host)
Packit Service 0a38ef
        # Save random password as it is not displayed by host-show
Packit Service 0a38ef
        if module.params.get('random'):
Packit Service 0a38ef
            randompassword = result['result']['randompassword']
Packit Service 0a38ef
        result = api.Command.host_show(fqdn)
Packit Service 0a38ef
        if module.params.get('random'):
Packit Service 0a38ef
            result['result']['randompassword'] = randompassword
Packit Service 0a38ef
        module.exit_json(changed=True, host=result['result'])
Packit Service 0a38ef
Packit Service 0a38ef
Packit Service 0a38ef
def ensure_host_absent(module, api, host):
Packit Service 0a38ef
    """
Packit Service 0a38ef
    Ensure host does not exist in IPA.
Packit Service 0a38ef
Packit Service 0a38ef
    :param module: the ansible module
Packit Service 0a38ef
    :param api: the IPA API handle
Packit Service 0a38ef
    :param host: the host information present in IPA, can be none if the
Packit Service 0a38ef
                 host does not exist
Packit Service 0a38ef
    """
Packit Service 0a38ef
    if not host:
Packit Service 0a38ef
        # Nothing to do, host already removed
Packit Service 0a38ef
        module.exit_json(changed=False)
Packit Service 0a38ef
Packit Service 0a38ef
    # Need to remove the host - only if not in check_mode
Packit Service 0a38ef
    if module.check_mode:
Packit Service 0a38ef
        module.exit_json(changed=True, host=host)
Packit Service 0a38ef
Packit Service 0a38ef
    fqdn = unicode(module.params.get('fqdn'))
Packit Service 0a38ef
    try:
Packit Service 0a38ef
        api.Command.host_del(fqdn)
Packit Service 0a38ef
    except Exception as e:
Packit Service 0a38ef
        module.fail_json(msg="Failed to remove host: %s" % e)
Packit Service 0a38ef
Packit Service 0a38ef
    module.exit_json(changed=True)
Packit Service 0a38ef
Packit Service 0a38ef
Packit Service 0a38ef
def main():
Packit Service 0a38ef
Packit Service 0a38ef
    module = AnsibleModule(
Packit Service 0a38ef
        argument_spec=dict(
Packit Service 0a38ef
            principal=dict(default='admin'),
Packit Service 0a38ef
            ccache=dict(required=False, type='path'),
Packit Service 0a38ef
            fqdn=dict(required=True),
Packit Service 0a38ef
            certificates=dict(required=False, type='list'),
Packit Service 0a38ef
            sshpubkey=dict(required=False),
Packit Service 0a38ef
            ipaddress=dict(required=False),
Packit Service 0a38ef
            random=dict(default=False, type='bool'),
Packit Service 0a38ef
            state=dict(default='present', choices=['present', 'absent']),
Packit Service 0a38ef
        ),
Packit Service 0a38ef
        supports_check_mode=True,
Packit Service 0a38ef
    )
Packit Service 0a38ef
Packit Service 0a38ef
    ccache = module.params.get('ccache')
Packit Service 0a38ef
    fqdn = unicode(module.params.get('fqdn'))
Packit Service 0a38ef
    state = module.params.get('state')
Packit Service 0a38ef
Packit Service 0a38ef
    try:
Packit Service 0a38ef
        os.environ['KRB5CCNAME'] = ccache
Packit Service 0a38ef
Packit Service 0a38ef
        cfg = dict(
Packit Service 0a38ef
            context='ansible_module',
Packit Service 0a38ef
            confdir=paths.ETC_IPA,
Packit Service 0a38ef
            in_server=False,
Packit Service 0a38ef
            debug=False,
Packit Service 0a38ef
            verbose=0,
Packit Service 0a38ef
        )
Packit Service 0a38ef
        api.bootstrap(**cfg)
Packit Service 0a38ef
        api.finalize()
Packit Service 0a38ef
        api.Backend.rpcclient.connect()
Packit Service 0a38ef
Packit Service 0a38ef
        try:
Packit Service 0a38ef
            result = api.Command.host_show(fqdn, all=True)
Packit Service 0a38ef
            host = result['result']
Packit Service 0a38ef
        except errors.NotFound:
Packit Service 0a38ef
            host = None
Packit Service 0a38ef
Packit Service 0a38ef
        if state in ['present', 'disabled']:
Packit Service 0a38ef
            ensure_host_present(module, api, host)
Packit Service 0a38ef
        elif state == 'absent':
Packit Service 0a38ef
            ensure_host_absent(module, api, host)
Packit Service 0a38ef
Packit Service 0a38ef
    except Exception as e:
Packit Service 0a38ef
        module.fail_json(msg="ipaclient_get_otp module failed : %s" % str(e))
Packit Service 0a38ef
    finally:
Packit Service 0a38ef
        run([paths.KDESTROY], raiseonerr=False, env=os.environ)
Packit Service 0a38ef
Packit Service 0a38ef
    module.exit_json(changed=False, host=host)
Packit Service 0a38ef
Packit Service 0a38ef
Packit Service 0a38ef
if __name__ == '__main__':
Packit Service 0a38ef
    main()