From 880eaf38a66e2d8bd80d1bf56c9eab9faa2d7363 Mon Sep 17 00:00:00 2001 From: Fabian Freyer Date: Tue, 11 Mar 2014 17:55:40 +0100 Subject: [PATCH 1/3] Added support for pkgng multiple repositories. Currently checking if pkgng >= 1.1.4, as specified in https://wiki.freebsd.org/pkgng . I guess that's when using PKGSITE was deprecated. --- library/packaging/pkgng | 42 +++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/library/packaging/pkgng b/library/packaging/pkgng index 7b0468a7cbd..f862b0b0df8 100644 --- a/library/packaging/pkgng +++ b/library/packaging/pkgng @@ -48,8 +48,11 @@ options: default: no pkgsite: description: - - specify packagesite to use for downloading packages, if - not specified, use settings from /usr/local/etc/pkg.conf + - for pkgng versions before 1.1.4, specify packagesite to use + for downloading packages, if not specified, use settings from + /usr/local/etc/pkg.conf + for newer pkgng versions, specify a the name of a repository + configured in /usr/local/etc/pkg/repos required: false author: bleader notes: @@ -68,6 +71,7 @@ EXAMPLES = ''' import json import shlex import os +import re import sys def query_package(module, pkgin_path, name): @@ -79,6 +83,22 @@ def query_package(module, pkgin_path, name): return False +def pkgng_older_than(module, pkgin_path, compare_version): + + rc, out, err = module.run_command("%s -v" % pkgin_path) + version = map(lambda x: int(x), re.split(r'[\._]', out)) + + i = 0 + new_pkgng = True + while compare_version[i] == version[i]: + i += 1 + if i == min(len(compare_version), len(version)): + break + else: + if compare_version[i] > version[i]: + new_pkgng = False + return not new_pkgng + def remove_packages(module, pkgin_path, packages): @@ -108,11 +128,18 @@ def install_packages(module, pkgin_path, packages, cached, pkgsite): install_c = 0 - if pkgsite != "": - pkgsite="PACKAGESITE=%s" % (pkgsite) + # as of pkg-1.1.4, PACKAGESITE is deprecated in favor of repository definitions + # in /usr/local/etc/pkg/repos + old_pkgng = pkgng_older_than(module, pkgin_path, [1, 1, 4]) + + if old_pkgng and (pkgsite != ""): + pkgsite = "PACKAGESITE=%s" % (pkgsite) if not module.check_mode and cached == "no": - rc, out, err = module.run_command("%s %s update" % (pkgsite, pkgin_path)) + if old_pkgng: + rc, out, err = module.run_command("%s %s update" % (pkgsite, pkgin_path)) + else: + rc, out, err = module.run_command("%s update" % (pkgin_path)) if rc != 0: module.fail_json(msg="Could not update catalogue") @@ -121,7 +148,10 @@ def install_packages(module, pkgin_path, packages, cached, pkgsite): continue if not module.check_mode: - rc, out, err = module.run_command("%s %s install -g -U -y %s" % (pkgsite, pkgin_path, package)) + if old_pkgng: + rc, out, err = module.run_command("%s %s install -g -U -y %s" % (pkgsite, pkgin_path, package)) + else: + rc, out, err = module.run_command("%s install -r %s -g -U -y %s" % (pkgin_path, pkgsite, package)) if not module.check_mode and not query_package(module, pkgin_path, package): module.fail_json(msg="failed to install %s: %s" % (package, out), stderr=err) From efe7bfa74a512edcba4fc435df2131d6f1ae9848 Mon Sep 17 00:00:00 2001 From: Fabian Freyer Date: Tue, 11 Mar 2014 18:03:14 +0100 Subject: [PATCH 2/3] Changed old variable names that referred to pkgin. --- library/packaging/pkgng | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/library/packaging/pkgng b/library/packaging/pkgng index f862b0b0df8..47e66328376 100644 --- a/library/packaging/pkgng +++ b/library/packaging/pkgng @@ -74,18 +74,18 @@ import os import re import sys -def query_package(module, pkgin_path, name): +def query_package(module, pkgng_path, name): - rc, out, err = module.run_command("%s info -g -e %s" % (pkgin_path, name)) + rc, out, err = module.run_command("%s info -g -e %s" % (pkgng_path, name)) if rc == 0: return True return False -def pkgng_older_than(module, pkgin_path, compare_version): +def pkgng_older_than(module, pkgng_path, compare_version): - rc, out, err = module.run_command("%s -v" % pkgin_path) + rc, out, err = module.run_command("%s -v" % pkgng_path) version = map(lambda x: int(x), re.split(r'[\._]', out)) i = 0 @@ -100,19 +100,19 @@ def pkgng_older_than(module, pkgin_path, compare_version): return not new_pkgng -def remove_packages(module, pkgin_path, packages): +def remove_packages(module, pkgng_path, packages): remove_c = 0 # Using a for loop incase of error, we can report the package that failed for package in packages: # Query the package first, to see if we even need to remove - if not query_package(module, pkgin_path, package): + if not query_package(module, pkgng_path, package): continue if not module.check_mode: - rc, out, err = module.run_command("%s delete -y %s" % (pkgin_path, package)) + rc, out, err = module.run_command("%s delete -y %s" % (pkgng_path, package)) - if not module.check_mode and query_package(module, pkgin_path, package): + if not module.check_mode and query_package(module, pkgng_path, package): module.fail_json(msg="failed to remove %s: %s" % (package, out)) remove_c += 1 @@ -124,36 +124,36 @@ def remove_packages(module, pkgin_path, packages): module.exit_json(changed=False, msg="package(s) already absent") -def install_packages(module, pkgin_path, packages, cached, pkgsite): +def install_packages(module, pkgng_path, packages, cached, pkgsite): install_c = 0 # as of pkg-1.1.4, PACKAGESITE is deprecated in favor of repository definitions # in /usr/local/etc/pkg/repos - old_pkgng = pkgng_older_than(module, pkgin_path, [1, 1, 4]) + old_pkgng = pkgng_older_than(module, pkgng_path, [1, 1, 4]) if old_pkgng and (pkgsite != ""): pkgsite = "PACKAGESITE=%s" % (pkgsite) if not module.check_mode and cached == "no": if old_pkgng: - rc, out, err = module.run_command("%s %s update" % (pkgsite, pkgin_path)) + rc, out, err = module.run_command("%s %s update" % (pkgsite, pkgng_path)) else: - rc, out, err = module.run_command("%s update" % (pkgin_path)) + rc, out, err = module.run_command("%s update" % (pkgng_path)) if rc != 0: module.fail_json(msg="Could not update catalogue") for package in packages: - if query_package(module, pkgin_path, package): + if query_package(module, pkgng_path, package): continue if not module.check_mode: if old_pkgng: - rc, out, err = module.run_command("%s %s install -g -U -y %s" % (pkgsite, pkgin_path, package)) + rc, out, err = module.run_command("%s %s install -g -U -y %s" % (pkgsite, pkgng_path, package)) else: - rc, out, err = module.run_command("%s install -r %s -g -U -y %s" % (pkgin_path, pkgsite, package)) + rc, out, err = module.run_command("%s install -r %s -g -U -y %s" % (pkgng_path, pkgsite, package)) - if not module.check_mode and not query_package(module, pkgin_path, package): + if not module.check_mode and not query_package(module, pkgng_path, package): module.fail_json(msg="failed to install %s: %s" % (package, out), stderr=err) install_c += 1 @@ -173,17 +173,17 @@ def main(): pkgsite = dict(default="", required=False)), supports_check_mode = True) - pkgin_path = module.get_bin_path('pkg', True) + pkgng_path = module.get_bin_path('pkg', True) p = module.params pkgs = p["name"].split(",") if p["state"] == "present": - install_packages(module, pkgin_path, pkgs, p["cached"], p["pkgsite"]) + install_packages(module, pkgng_path, pkgs, p["cached"], p["pkgsite"]) elif p["state"] == "absent": - remove_packages(module, pkgin_path, pkgs) + remove_packages(module, pkgng_path, pkgs) # import module snippets from ansible.module_utils.basic import * From 064722aa5e199776451de0528350511826aa5899 Mon Sep 17 00:00:00 2001 From: Fabian Freyer Date: Tue, 11 Mar 2014 20:24:32 +0100 Subject: [PATCH 3/3] Added support for package annotations. --- library/packaging/pkgng | 121 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 114 insertions(+), 7 deletions(-) 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 *