diff --git a/library/packaging/pkgng b/library/packaging/pkgng index 47e66328376..5bf8fb650f0 100644 --- a/library/packaging/pkgng +++ b/library/packaging/pkgng @@ -46,6 +46,14 @@ options: choices: [ 'yes', 'no' ] required: false default: no + annotation: + description: + - a comma-separated list of keyvalue-pairs of the form + <+/-/:>[=]. A '+' denotes adding an annotation, a + '-' denotes removing an annotation, and ':' denotes modifying an + annotation. + If setting or modifying annotations, a value must be provided. + required: false pkgsite: description: - for pkgng versions before 1.1.4, specify packagesite to use @@ -63,6 +71,9 @@ EXAMPLES = ''' # Install package foo - pkgng: name=foo state=present +# Annotate package foo and bar +- pkgng: name=foo,bar annotation=+test1=baz,-test2,:test3=foobar + # Remove packages foo and bar - pkgng: name=foo,bar state=absent ''' @@ -119,9 +130,9 @@ def remove_packages(module, pkgng_path, packages): if remove_c > 0: - module.exit_json(changed=True, msg="removed %s package(s)" % remove_c) + return (True, "removed %s package(s)" % remove_c) - module.exit_json(changed=False, msg="package(s) already absent") + return (False, "package(s) already absent") def install_packages(module, pkgng_path, packages, cached, pkgsite): @@ -159,17 +170,97 @@ def install_packages(module, pkgng_path, packages, cached, pkgsite): install_c += 1 if install_c > 0: - module.exit_json(changed=True, msg="present %s package(s)" % (install_c)) + return (True, "added %s package(s)" % (install_c)) - module.exit_json(changed=False, msg="package(s) already present") + return (False, "package(s) already present") +def annotation_query(module, pkgng_path, package, tag): + rc, out, err = module.run_command("%s info -g -A %s" % (pkgng_path, package)) + match = re.search(r'^\s*(?P%s)\s*:\s*(?P\w+)' % tag, out, flags=re.MULTILINE) + if match: + return match.group('value') + return False + + +def annotation_add(module, pkgng_path, package, tag, value): + _value = annotation_query(module, pkgng_path, package, tag) + if not _value: + # Annotation does not exist, add it. + rc, out, err = module.run_command('%s annotate -y -A %s %s "%s"' + % (pkgng_path, package, tag, value)) + if rc != 0: + module.fail_json("could not annotate %s: %s" + % (package, out), stderr=err) + return True + elif _value != value: + # Annotation exists, but value differs + module.fail_json( + mgs="failed to annotate %s, because %s is already set to %s, but should be set to %s" + % (package, tag, _value, value)) + return False + else: + # Annotation exists, nothing to do + return False + +def annotation_delete(module, pkgng_path, package, tag, value): + _value = annotation_query(module, pkgng_path, package, tag) + if _value: + rc, out, err = module.run_command('%s annotate -y -D %s %s' + % (pkgng_path, package, tag)) + if rc != 0: + module.fail_json("could not delete annotation to %s: %s" + % (package, out), stderr=err) + return True + return False + +def annotation_modify(module, pkgng_path, package, tag, value): + _value = annotation_query(module, pkgng_path, package, tag) + if not value: + # No such tag + module.fail_json("could not change annotation to %s: tag %s does not exist" + % (package, tag)) + elif _value == value: + # No change in value + return False + else: + rc,out,err = module.run_command('%s annotate -y -M %s %s "%s"' + % (pkgng_path, package, tag, value)) + if rc != 0: + module.fail_json("could not change annotation annotation to %s: %s" + % (package, out), stderr=err) + return True + + +def annotate_packages(module, pkgng_path, packages, annotation): + annotate_c = 0 + annotations = map(lambda _annotation: + re.match(r'(?P[\+-:])(?P\w+)(=(?P\w+))?', + _annotation).groupdict(), + re.split(r',', annotation)) + + operation = { + '+': annotation_add, + '-': annotation_delete, + ':': annotation_modify + } + + for package in packages: + for _annotation in annotations: + annotate_c += ( 1 if operation[_annotation['operation']]( + module, pkgng_path, package, + _annotation['tag'], _annotation['value']) else 0 ) + + if annotate_c > 0: + return (True, "added %s annotations." % annotate_c) + return (False, "changed no annotations") def main(): module = AnsibleModule( argument_spec = dict( - state = dict(default="present", choices=["present","absent"]), + state = dict(default="present", choices=["present","absent"], required=False), name = dict(aliases=["pkg"], required=True), cached = dict(default=False, type='bool'), + annotation = dict(default="", required=False), pkgsite = dict(default="", required=False)), supports_check_mode = True) @@ -179,11 +270,27 @@ def main(): pkgs = p["name"].split(",") + changed = False + msgs = [] + if p["state"] == "present": - install_packages(module, pkgng_path, pkgs, p["cached"], p["pkgsite"]) + _changed, _msg = install_packages(module, pkgng_path, pkgs, p["cached"], p["pkgsite"]) + changed = changed or _changed + msgs.append(_msg) elif p["state"] == "absent": - remove_packages(module, pkgng_path, pkgs) + _changed, _msg = remove_packages(module, pkgng_path, pkgs) + changed = changed or _changed + msgs.append(_msg) + + if p["annotation"]: + _changed, _msg = annotate_packages(module, pkgng_path, pkgs, p["annotation"]) + changed = changed or _changed + msgs.append(_msg) + + module.exit_json(changed=changed, msg=", ".join(msgs)) + + # import module snippets from ansible.module_utils.basic import *