|
|
6e61fb |
#!/usr/bin/python
|
|
|
6e61fb |
from ansible.module_utils.basic import AnsibleModule
|
|
|
6e61fb |
from ansible.module_utils import common_koji
|
|
|
6e61fb |
|
|
|
6e61fb |
|
|
|
6e61fb |
ANSIBLE_METADATA = {
|
|
|
6e61fb |
'metadata_version': '1.0',
|
|
|
6e61fb |
'status': ['preview'],
|
|
|
6e61fb |
'supported_by': 'community'
|
|
|
6e61fb |
}
|
|
|
6e61fb |
|
|
|
6e61fb |
|
|
|
6e61fb |
DOCUMENTATION = '''
|
|
|
6e61fb |
---
|
|
|
6e61fb |
module: koji_tag_inheritance
|
|
|
6e61fb |
|
|
|
6e61fb |
short_description: Manage a Koji tag inheritance relationship
|
|
|
6e61fb |
description:
|
|
|
6e61fb |
- Fine-grained management for tag inheritance relationships.
|
|
|
6e61fb |
- The `koji_tag` module is all-or-nothing when it comes to managing tag
|
|
|
6e61fb |
inheritance. When you set inheritance with `koji_tag`, the module will
|
|
|
6e61fb |
delete any inheritance relationships that are not defined there.
|
|
|
6e61fb |
- In some cases you may want to declare *some* inheritance relationships
|
|
|
6e61fb |
within Ansible without clobbering other existing inheritance
|
|
|
6e61fb |
relationships. For example, `MBS
|
|
|
6e61fb |
<https://fedoraproject.org/wiki/Changes/ModuleBuildService>`_ will
|
|
|
6e61fb |
dynamically manage some inheritance relationships of tags.
|
|
|
6e61fb |
options:
|
|
|
6e61fb |
child_tag:
|
|
|
6e61fb |
description:
|
|
|
6e61fb |
- The name of the Koji tag that will be the child.
|
|
|
6e61fb |
required: true
|
|
|
6e61fb |
parent_tag:
|
|
|
6e61fb |
description:
|
|
|
6e61fb |
- The name of the Koji tag that will be the parent of the child.
|
|
|
6e61fb |
required: true
|
|
|
6e61fb |
priority:
|
|
|
6e61fb |
description:
|
|
|
6e61fb |
- The priority of this parent for this child. Parents with smaller
|
|
|
6e61fb |
numbers will override parents with bigger numbers.
|
|
|
6e61fb |
- 'When defining an inheritance relationship with "state: present", you
|
|
|
6e61fb |
must specify a priority. When deleting an inheritance relationship
|
|
|
6e61fb |
with "state: absent", you should not specify a priority. Ansible will
|
|
|
6e61fb |
simply remove the parent_tag link, regardless of its priority.'
|
|
|
6e61fb |
required: true
|
|
|
6e61fb |
maxdepth:
|
|
|
6e61fb |
description:
|
|
|
6e61fb |
- By default, a tag's inheritance chain is unlimited. This means that
|
|
|
6e61fb |
Koji will look back through an unlimited chain of parent and
|
|
|
6e61fb |
grandparent tags to determine the contents of the tag.
|
|
|
6e61fb |
- You may use this maxdepth parameter to limit the maximum depth of the
|
|
|
6e61fb |
inheritance. For example "0" means that only the parent tag itself
|
|
|
6e61fb |
will be available in the inheritance - parent tags of the parent tag
|
|
|
6e61fb |
won't be available.
|
|
|
6e61fb |
- 'To restore the default umlimited depth behavior on a tag, you can set
|
|
|
6e61fb |
``maxdepth: null`` or ``maxdepth: `` (empty value).'
|
|
|
6e61fb |
- If you do not set any ``maxdepth`` parameter at all, koji-ansible
|
|
|
6e61fb |
will overwrite an existing tag's current maxdepth setting to "null"
|
|
|
6e61fb |
(in other words, unlimited depth). This was the historical behavior
|
|
|
6e61fb |
of the module and the easiest way to implement this in the code.
|
|
|
6e61fb |
Arguably this behavior is unexpected, because Ansible should only do
|
|
|
6e61fb |
what you tell it to do. We might change this in the future so that
|
|
|
6e61fb |
Ansible only modifies ``maxdepth`` *if* you explicitly configure it.
|
|
|
6e61fb |
Please open GitHub issues to discuss your use-case.
|
|
|
6e61fb |
required: false
|
|
|
6e61fb |
default: null (unlimited depth)
|
|
|
6e61fb |
pkg_filter:
|
|
|
6e61fb |
description:
|
|
|
6e61fb |
- Regular expression selecting the packages for which builds can be
|
|
|
6e61fb |
inherited through this inheritance link.
|
|
|
6e61fb |
- Don't forget to use ``^`` and ``$`` when limiting to exact package
|
|
|
6e61fb |
names; they are not implicit.
|
|
|
6e61fb |
- The default empty string allows all packages to be inherited through
|
|
|
6e61fb |
this link.
|
|
|
6e61fb |
required: false
|
|
|
6e61fb |
default: ''
|
|
|
6e61fb |
intransitive:
|
|
|
6e61fb |
description:
|
|
|
6e61fb |
- Prevents inheritance link from being used by the child tag's children.
|
|
|
6e61fb |
In other words, the link is only used to determine parent tags for the
|
|
|
6e61fb |
child tag directly, but not to determine "grandparent" tags for the
|
|
|
6e61fb |
child tag's children.
|
|
|
6e61fb |
required: false
|
|
|
6e61fb |
default: false
|
|
|
6e61fb |
noconfig:
|
|
|
6e61fb |
description:
|
|
|
6e61fb |
- Prevents tag options ("extra") from being inherited.
|
|
|
6e61fb |
required: false
|
|
|
6e61fb |
default: false
|
|
|
6e61fb |
state:
|
|
|
6e61fb |
description:
|
|
|
6e61fb |
- Whether to add or remove this inheritance link.
|
|
|
6e61fb |
choices: [present, absent]
|
|
|
6e61fb |
default: present
|
|
|
6e61fb |
requirements:
|
|
|
6e61fb |
- "python >= 2.7"
|
|
|
6e61fb |
- "koji"
|
|
|
6e61fb |
'''
|
|
|
6e61fb |
|
|
|
6e61fb |
EXAMPLES = '''
|
|
|
6e61fb |
- name: Use devtoolset to build for Ceph Nautilus
|
|
|
6e61fb |
hosts: localhost
|
|
|
6e61fb |
tasks:
|
|
|
6e61fb |
- name: set devtoolset-7 as a parent of ceph nautilus
|
|
|
6e61fb |
koji_tag_inheritance:
|
|
|
6e61fb |
koji: kojidev
|
|
|
6e61fb |
parent_tag: sclo7-devtoolset-7-rh-release
|
|
|
6e61fb |
child_tag: storage7-ceph-nautilus-el7-build
|
|
|
6e61fb |
priority: 25
|
|
|
6e61fb |
|
|
|
6e61fb |
- name: remove devtoolset-7 as a parent of my other build tag
|
|
|
6e61fb |
koji_tag_inheritance:
|
|
|
6e61fb |
parent_tag: sclo7-devtoolset-7-rh-release
|
|
|
6e61fb |
child_tag: other-storage-el7-build
|
|
|
6e61fb |
state: absent
|
|
|
6e61fb |
'''
|
|
|
6e61fb |
|
|
|
6e61fb |
RETURN = ''' # '''
|
|
|
6e61fb |
|
|
|
6e61fb |
|
|
|
6e61fb |
def get_ids_and_inheritance(session, child_tag, parent_tag):
|
|
|
6e61fb |
"""
|
|
|
6e61fb |
Query Koji for the current state of these tags and inheritance.
|
|
|
6e61fb |
|
|
|
6e61fb |
:param session: Koji client session
|
|
|
6e61fb |
:param str child_tag: Koji tag name
|
|
|
6e61fb |
:param str parent_tag: Koji tag name
|
|
|
6e61fb |
:return: 3-element tuple of child_id (int), parent_id (int),
|
|
|
6e61fb |
and current_inheritance (list)
|
|
|
6e61fb |
"""
|
|
|
6e61fb |
child_taginfo = session.getTag(child_tag)
|
|
|
6e61fb |
parent_taginfo = session.getTag(parent_tag)
|
|
|
6e61fb |
child_id = child_taginfo['id'] if child_taginfo else None
|
|
|
6e61fb |
parent_id = parent_taginfo['id'] if parent_taginfo else None
|
|
|
6e61fb |
if child_id:
|
|
|
6e61fb |
current_inheritance = session.getInheritanceData(child_id)
|
|
|
6e61fb |
else:
|
|
|
6e61fb |
current_inheritance = []
|
|
|
6e61fb |
# TODO use multicall to get all of this at once:
|
|
|
6e61fb |
# (Need to update the test suite fakes to handle multicalls)
|
|
|
6e61fb |
# session.multicall = True
|
|
|
6e61fb |
# session.getTag(child_tag, strict=True)
|
|
|
6e61fb |
# session.getTag(parent_tag, strict=True)
|
|
|
6e61fb |
# session.getInheritanceData(child_tag)
|
|
|
6e61fb |
# multicall_results = session.multiCall(strict=True)
|
|
|
6e61fb |
# # flatten multicall results:
|
|
|
6e61fb |
# multicall_results = [result[0] for result in multicall_results]
|
|
|
6e61fb |
# child_id = multicall_results[0]['id']
|
|
|
6e61fb |
# parent_id = multicall_results[1]['id']
|
|
|
6e61fb |
# current_inheritance = multicall_results[2]
|
|
|
6e61fb |
return (child_id, parent_id, current_inheritance)
|
|
|
6e61fb |
|
|
|
6e61fb |
|
|
|
6e61fb |
def generate_new_rule(child_id, parent_tag, parent_id, priority, maxdepth,
|
|
|
6e61fb |
pkg_filter, intransitive, noconfig):
|
|
|
6e61fb |
"""
|
|
|
6e61fb |
Return a full inheritance rule to add for this child tag.
|
|
|
6e61fb |
|
|
|
6e61fb |
:param int child_id: Koji tag id
|
|
|
6e61fb |
:param str parent_tag: Koji tag name
|
|
|
6e61fb |
:param int parent_id: Koji tag id
|
|
|
6e61fb |
:param int priority: Priority of this parent for this child
|
|
|
6e61fb |
:param int maxdepth: Max depth of the inheritance
|
|
|
6e61fb |
:param str pkg_filter: Regular expression string of package names to include
|
|
|
6e61fb |
:param bool intransitive: Don't allow this inheritance link to be inherited
|
|
|
6e61fb |
:param bool noconfig: Prevent tag options ("extra") from being inherited
|
|
|
6e61fb |
"""
|
|
|
6e61fb |
return {
|
|
|
6e61fb |
'child_id': child_id,
|
|
|
6e61fb |
'intransitive': intransitive,
|
|
|
6e61fb |
'maxdepth': maxdepth,
|
|
|
6e61fb |
'name': parent_tag,
|
|
|
6e61fb |
'noconfig': noconfig,
|
|
|
6e61fb |
'parent_id': parent_id,
|
|
|
6e61fb |
'pkg_filter': pkg_filter,
|
|
|
6e61fb |
'priority': priority}
|
|
|
6e61fb |
|
|
|
6e61fb |
|
|
|
6e61fb |
def add_tag_inheritance(session, child_tag, parent_tag, priority, maxdepth,
|
|
|
6e61fb |
pkg_filter, intransitive, noconfig, check_mode):
|
|
|
6e61fb |
"""
|
|
|
6e61fb |
Ensure that a tag inheritance rule exists.
|
|
|
6e61fb |
|
|
|
6e61fb |
:param session: Koji client session
|
|
|
6e61fb |
:param str child_tag: Koji tag name
|
|
|
6e61fb |
:param str parent_tag: Koji tag name
|
|
|
6e61fb |
:param int priority: Priority of this parent for this child
|
|
|
6e61fb |
:param int maxdepth: Max depth of the inheritance
|
|
|
6e61fb |
:param str pkg_filter: Regular expression string of package names to include
|
|
|
6e61fb |
:param bool intransitive: Don't allow this inheritance link to be inherited
|
|
|
6e61fb |
:param bool noconfig: Prevent tag options ("extra") from being inherited
|
|
|
6e61fb |
:param bool check_mode: don't make any changes
|
|
|
6e61fb |
:return: result (dict)
|
|
|
6e61fb |
"""
|
|
|
6e61fb |
result = {'changed': False, 'stdout_lines': []}
|
|
|
6e61fb |
data = get_ids_and_inheritance(session, child_tag, parent_tag)
|
|
|
6e61fb |
child_id, parent_id, current_inheritance = data
|
|
|
6e61fb |
if not child_id:
|
|
|
6e61fb |
msg = 'child tag %s not found' % child_tag
|
|
|
6e61fb |
if check_mode:
|
|
|
6e61fb |
result['stdout_lines'].append(msg)
|
|
|
6e61fb |
else:
|
|
|
6e61fb |
raise ValueError(msg)
|
|
|
6e61fb |
if not parent_id:
|
|
|
6e61fb |
msg = 'parent tag %s not found' % parent_tag
|
|
|
6e61fb |
if check_mode:
|
|
|
6e61fb |
result['stdout_lines'].append(msg)
|
|
|
6e61fb |
else:
|
|
|
6e61fb |
raise ValueError(msg)
|
|
|
6e61fb |
|
|
|
6e61fb |
new_rule = generate_new_rule(child_id, parent_tag, parent_id, priority,
|
|
|
6e61fb |
maxdepth, pkg_filter, intransitive, noconfig)
|
|
|
6e61fb |
new_rules = [new_rule]
|
|
|
6e61fb |
for rule in current_inheritance:
|
|
|
6e61fb |
if rule == new_rule:
|
|
|
6e61fb |
return result
|
|
|
6e61fb |
if rule['priority'] == priority:
|
|
|
6e61fb |
# prefix taginfo-style inheritance strings with diff-like +/-
|
|
|
6e61fb |
result['stdout_lines'].append('dissimilar rules:')
|
|
|
6e61fb |
result['stdout_lines'].extend(
|
|
|
6e61fb |
map(lambda r: ' -' + r,
|
|
|
6e61fb |
common_koji.describe_inheritance_rule(rule)))
|
|
|
6e61fb |
result['stdout_lines'].extend(
|
|
|
6e61fb |
map(lambda r: ' +' + r,
|
|
|
6e61fb |
common_koji.describe_inheritance_rule(new_rule)))
|
|
|
6e61fb |
delete_rule = rule.copy()
|
|
|
6e61fb |
# Mark this rule for deletion
|
|
|
6e61fb |
delete_rule['delete link'] = True
|
|
|
6e61fb |
new_rules.insert(0, delete_rule)
|
|
|
6e61fb |
|
|
|
6e61fb |
if len(new_rules) > 1:
|
|
|
6e61fb |
result['stdout_lines'].append('remove inheritance link:')
|
|
|
6e61fb |
result['stdout_lines'].extend(
|
|
|
6e61fb |
common_koji.describe_inheritance(new_rules[:-1]))
|
|
|
6e61fb |
result['stdout_lines'].append('add inheritance link:')
|
|
|
6e61fb |
result['stdout_lines'].extend(
|
|
|
6e61fb |
common_koji.describe_inheritance_rule(new_rule))
|
|
|
6e61fb |
result['changed'] = True
|
|
|
6e61fb |
if not check_mode:
|
|
|
6e61fb |
common_koji.ensure_logged_in(session)
|
|
|
6e61fb |
session.setInheritanceData(child_tag, new_rules)
|
|
|
6e61fb |
return result
|
|
|
6e61fb |
|
|
|
6e61fb |
|
|
|
6e61fb |
def remove_tag_inheritance(session, child_tag, parent_tag, check_mode):
|
|
|
6e61fb |
"""
|
|
|
6e61fb |
Ensure that a tag inheritance rule does not exist.
|
|
|
6e61fb |
|
|
|
6e61fb |
:param session: Koji client session
|
|
|
6e61fb |
:param str child_tag: Koji tag name
|
|
|
6e61fb |
:param str parent_tag: Koji tag name
|
|
|
6e61fb |
:param bool check_mode: don't make any changes
|
|
|
6e61fb |
:return: result (dict)
|
|
|
6e61fb |
"""
|
|
|
6e61fb |
result = {'changed': False, 'stdout_lines': []}
|
|
|
6e61fb |
current_inheritance = session.getInheritanceData(child_tag)
|
|
|
6e61fb |
found_rule = {}
|
|
|
6e61fb |
for rule in current_inheritance:
|
|
|
6e61fb |
if rule['name'] == parent_tag:
|
|
|
6e61fb |
found_rule = rule.copy()
|
|
|
6e61fb |
# Mark this rule for deletion
|
|
|
6e61fb |
found_rule['delete link'] = True
|
|
|
6e61fb |
if not found_rule:
|
|
|
6e61fb |
return result
|
|
|
6e61fb |
result['stdout_lines'].append('remove inheritance link:')
|
|
|
6e61fb |
result['stdout_lines'].extend(
|
|
|
6e61fb |
common_koji.describe_inheritance_rule(found_rule))
|
|
|
6e61fb |
result['changed'] = True
|
|
|
6e61fb |
if not check_mode:
|
|
|
6e61fb |
common_koji.ensure_logged_in(session)
|
|
|
6e61fb |
session.setInheritanceData(child_tag, [found_rule])
|
|
|
6e61fb |
return result
|
|
|
6e61fb |
|
|
|
6e61fb |
|
|
|
6e61fb |
def run_module():
|
|
|
6e61fb |
module_args = dict(
|
|
|
6e61fb |
koji=dict(type='str', required=False),
|
|
|
6e61fb |
child_tag=dict(type='str', required=True),
|
|
|
6e61fb |
parent_tag=dict(type='str', required=True),
|
|
|
6e61fb |
priority=dict(type='int', required=False),
|
|
|
6e61fb |
maxdepth=dict(type='int', required=False, default=None),
|
|
|
6e61fb |
pkg_filter=dict(type='str', required=False, default=''),
|
|
|
6e61fb |
intransitive=dict(type='bool', required=False, default=False),
|
|
|
6e61fb |
noconfig=dict(type='bool', required=False, default=False),
|
|
|
6e61fb |
state=dict(type='str', choices=[
|
|
|
6e61fb |
'present', 'absent'], required=False, default='present'),
|
|
|
6e61fb |
)
|
|
|
6e61fb |
module = AnsibleModule(
|
|
|
6e61fb |
argument_spec=module_args,
|
|
|
6e61fb |
supports_check_mode=True
|
|
|
6e61fb |
)
|
|
|
6e61fb |
|
|
|
6e61fb |
if not common_koji.HAS_KOJI:
|
|
|
6e61fb |
module.fail_json(msg='koji is required for this module')
|
|
|
6e61fb |
|
|
|
6e61fb |
check_mode = module.check_mode
|
|
|
6e61fb |
params = module.params
|
|
|
6e61fb |
state = params['state']
|
|
|
6e61fb |
profile = params['koji']
|
|
|
6e61fb |
|
|
|
6e61fb |
session = common_koji.get_session(profile)
|
|
|
6e61fb |
|
|
|
6e61fb |
if state == 'present':
|
|
|
6e61fb |
if 'priority' not in params:
|
|
|
6e61fb |
module.fail_json(msg='specify a "priority" integer')
|
|
|
6e61fb |
result = add_tag_inheritance(session,
|
|
|
6e61fb |
child_tag=params['child_tag'],
|
|
|
6e61fb |
parent_tag=params['parent_tag'],
|
|
|
6e61fb |
priority=params['priority'],
|
|
|
6e61fb |
maxdepth=params['maxdepth'],
|
|
|
6e61fb |
pkg_filter=params['pkg_filter'],
|
|
|
6e61fb |
intransitive=params['intransitive'],
|
|
|
6e61fb |
noconfig=params['noconfig'],
|
|
|
6e61fb |
check_mode=check_mode)
|
|
|
6e61fb |
elif state == 'absent':
|
|
|
6e61fb |
result = remove_tag_inheritance(session,
|
|
|
6e61fb |
child_tag=params['child_tag'],
|
|
|
6e61fb |
parent_tag=params['parent_tag'],
|
|
|
6e61fb |
check_mode=check_mode)
|
|
|
6e61fb |
|
|
|
6e61fb |
module.exit_json(**result)
|
|
|
6e61fb |
|
|
|
6e61fb |
|
|
|
6e61fb |
def main():
|
|
|
6e61fb |
run_module()
|
|
|
6e61fb |
|
|
|
6e61fb |
|
|
|
6e61fb |
if __name__ == '__main__':
|
|
|
6e61fb |
main()
|