bstinson / centos / releng

Forked from centos/releng 3 years ago
Clone
Blob Blame History Raw
#!/usr/bin/python
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils import common_koji


ANSIBLE_METADATA = {
    'metadata_version': '1.0',
    'status': ['preview'],
    'supported_by': 'community'
}


DOCUMENTATION = '''
---
module: koji_host

short_description: Create and manage Koji build hosts
description:
   - This module can add new hosts and manage existing hosts.
   - 'Koji only supports adding new hosts, not deleting them. Once they are
     defined, you can enable or disable the hosts with "state: enabled" or
     "state: disabled".'

options:
   name:
     description:
       - The name of the Koji builder.
       - 'Example: "builder1.example.com".'
     required: true
   arches:
     description:
       - The list of arches this host supports.
       - 'Example: [x86_64]'
     required: true
   channels:
     description:
       - The list of channels this host should belong to.
       - If you specify a completely new channel here, Ansible will create the
         channel on the hub. For example, when you set up OSBS with Koji, you
         must add a builder host to a new "container" channel. You can simply
         specify "container" in the list here, and Ansible will create the new
         "container" channel when it adds your host to that channel.
       - 'Example: [default, createrepo]'
     required: false
   state:
     description:
       - Whether to set this host as "enabled" or "disabled". If unset, this
         defaults to "enabled".
   krb_principal:
     description:
       - Set a non-default krb principal for this host. If unset, Koji will
         use the standard krb principal scheme for builder accounts.
   capacity:
     description:
       - Total task weight for this host. This is a float value. If unset,
         Koji will use the standard capacity for a host (2.0).
       - 'Example: 10.0'
   description:
     description:
       - Human-readable description for this host.
   comment:
     description:
       - Human-readable comment explaining the current state of the host. You
         may write a description here explaining how this host was set up, or
         why this host is currently offline.
requirements:
  - "python >= 2.7"
  - "koji"
'''

EXAMPLES = '''
- name: create a koji host
  hosts: localhost
  tasks:
    - name: Add new builder1 host
      koji_host:
        name: builder1.example.com
        arches: [x86_64]
        state: enabled
        channels:
          - createrepo
          - default

    - name: Add new builder host for OSBS
      koji_host:
        name: containerbuild1.example.com
        arches: [x86_64]
        state: enabled
        channels:
          # This will automatically create the "container" channel
          # if it does not already exist:
          - container
'''


def ensure_channels(session, host_id, host_name, check_mode, desired_channels):
    """
    Ensure that given host belongs to given channels (and only them).

    :param session: Koji client session
    :param int host_id: Koji host ID
    :param int host_name: Koji host name
    :param bool check_mode: don't make any changes
    :param list desired_channels: channels that the host should belong to
    """
    result = {'changed': False, 'stdout_lines': []}
    common_koji.ensure_logged_in(session)
    current_channels = session.listChannels(host_id)
    current_channels = [channel['name'] for channel in current_channels]
    for channel in current_channels:
        if channel not in desired_channels:
            if not check_mode:
                session.removeHostFromChannel(host_name, channel)
            result['stdout_lines'].append('removed host from channel %s' % channel)
            result['changed'] = True
    for channel in desired_channels:
        if channel not in current_channels:
            if not check_mode:
                session.addHostToChannel(host_name, channel, create=True)
            result['stdout_lines'].append('added host to channel %s' % channel)
            result['changed'] = True
    return result


def ensure_host(session, name, check_mode, state, arches, krb_principal,
                channels, **kwargs):
    """
    Ensure that this host is configured in Koji.

    :param session: Koji client session
    :param str name: Koji builder host name
    :param bool check_mode: don't make any changes
    :param str state: "enabled" or "disabled"
    :param list arches: list of arches for this builder.
    :param str krb_principal: custom kerberos principal, or None
    :param list chanels: list of channels this host should belong to.
    :param **kwargs: Pass remaining kwargs directly into Koji's editHost RPC.
    """
    result = {'changed': False, 'stdout_lines': []}
    host = session.getHost(name)
    if not host:
        result['changed'] = True
        result['stdout_lines'].append('created host')
        if check_mode:
            return result
        common_koji.ensure_logged_in(session)
        id_ = session.addHost(name, arches, krb_principal)
        host = session.getHost(id_)
    if state == 'enabled':
        if not host['enabled']:
            result['changed'] = True
            result['stdout_lines'].append('enabled host')
            if not check_mode:
                common_koji.ensure_logged_in(session)
                session.enableHost(name)
    elif state == 'disabled':
        if host['enabled']:
            result['changed'] = True
            result['stdout_lines'].append('disabled host')
            if not check_mode:
                common_koji.ensure_logged_in(session)
                session.disableHost(name)
    edits = {}
    if ' '.join(arches) != host['arches']:
        edits['arches'] = ' '.join(arches)
    for key, value in kwargs.items():
        if value is None:
            continue  # Ansible did not set this parameter.
        if key in host and kwargs[key] != host[key]:
            edits[key] = value
    if edits:
        result['changed'] = True
        for edit in edits.keys():
            result['stdout_lines'].append('edited host %s' % edit)
        if not check_mode:
            common_koji.ensure_logged_in(session)
            session.editHost(name, **edits)

    # Ensure host is member of desired channels.
    if channels not in (None, ''):
        channels_result = ensure_channels(session, host['id'],
                                          name, check_mode, channels)
        if channels_result['changed']:
            result['changed'] = True
        result['stdout_lines'].extend(channels_result['stdout_lines'])

    return result


def run_module():
    module_args = dict(
        koji=dict(type='str', required=False),
        name=dict(type='str', required=True),
        arches=dict(type='list', required=True),
        channels=dict(type='list', required=False, default=None),
        krb_principal=dict(type='str', required=False, default=None),
        capacity=dict(type='float', required=False, default=None),
        description=dict(type='str', required=False, default=None),
        comment=dict(type='str', required=False, default=None),
        state=dict(type='str', choices=[
                   'enabled', 'disabled'], required=False, default='enabled'),
    )
    module = AnsibleModule(
        argument_spec=module_args,
        supports_check_mode=True
    )

    if not common_koji.HAS_KOJI:
        module.fail_json(msg='koji is required for this module')

    check_mode = module.check_mode
    params = module.params
    profile = params['koji']
    name = params['name']
    state = params['state']

    session = common_koji.get_session(profile)

    result = ensure_host(session, name, check_mode, state,
                         arches=params['arches'],
                         channels=params['channels'],
                         krb_principal=params['krb_principal'],
                         capacity=params['capacity'],
                         description=params['description'],
                         comment=params['comment'])
    module.exit_json(**result)


def main():
    run_module()


if __name__ == '__main__':
    main()