From eb6398f16877088c0591db66fd48d39b4444ada1 Mon Sep 17 00:00:00 2001 From: Patrik Lundin Date: Sun, 3 Mar 2013 00:01:02 +0100 Subject: [PATCH] Add openbsd_pkg: package management for OpenBSD. * Code influenced by the service module but without classes. * Thanks to @dhozac for input and review. * Thanks to @johanwiren for getting me to push this out the door. --- library/openbsd_pkg | 222 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 library/openbsd_pkg diff --git a/library/openbsd_pkg b/library/openbsd_pkg new file mode 100644 index 0000000000..52891e81d0 --- /dev/null +++ b/library/openbsd_pkg @@ -0,0 +1,222 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2013, Patrik Lundin +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +import re + +DOCUMENTATION = ''' +--- +module: openbsd_pkg +author: Patrik Lundin +version_added: 1.1 +short_description: Manage packages on OpenBSD. +description: + - Manage packages on OpenBSD using the pkg tools. +options: + name: + required: true + description: + - Name of the package. + state: + required: true + choices: [ present, latest, absent ] + description: + C(present) will make sure the package is installed. + C(latest) will make sure the latest version of the package is installed. + C(absent) will make sure the specified package is not installed. +examples: + - description: Make sure nmap is installed + code: "openbsd_pkg: name=nmap state=present" + - description: Make sure nmap is the latest version + code: "openbsd_pkg: name=nmap state=latest" + - description: Make sure nmap is not installed + code: "openbsd_pkg: name=nmap state=absent" +''' + +# select whether we dump additional debug info through syslog +syslogging = False + +# Function used for executing commands. +def execute_command(cmd, syslogging): + if syslogging: + syslog.openlog('ansible-%s' % os.path.basename(__file__)) + syslog.syslog(syslog.LOG_NOTICE, 'Command %s' % '|'.join(cmd)) + + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (out, err) = p.communicate() + rc = p.returncode + return (rc, out, err) + +# Function used for getting the name of a currently installed package. +def get_current_name(name, specific_version): + info_cmd = 'pkg_info' + (rc, stdout, stderr) = execute_command("%s" % (info_cmd), syslogging) + if rc != 0: + return (rc, stdout, stderr) + + if specific_version: + syntax = "%s" + else: + syntax = "%s-" + + for line in stdout.splitlines(): + if syntax % name in line: + current_name = line.split()[0] + + return current_name + +# Function used to find out if a package is currently installed. +def get_package_state(name, specific_version): + info_cmd = 'pkg_info -e' + + if specific_version: + syntax = "%s %s" + else: + syntax = "%s %s-*" + + rc, stdout, stderr = execute_command(syntax % (info_cmd, name), syslogging) + + if rc == 0: + return True + else: + return False + +# Function used to make sure a package is present. +def package_present(name, installed_state): + install_cmd = 'pkg_add -I' + if installed_state is False: + rc, stdout, stderr = execute_command("%s %s" % (install_cmd, name), syslogging) + # pkg_add returns 0 even if the package does not exist + # so depend on stderr instead if something bad happened. + if stderr: + rc = 1 + changed=False + else: + changed=True + else: + rc = 0 + stdout = '' + stderr = '' + changed=False + + return (rc, stdout, stderr, changed) + +# Function used to make sure a package is the latest available version. +def package_latest(name, installed_state, specific_version): + + upgrade_cmd = 'pkg_add -u' + pre_upgrade_name = '' + post_upgrade_name = '' + if installed_state is True: + + # pkg_add -u exits 0 even if no update was needed, so compare the + # installed package before and after to know if we changed anything. + pre_upgrade_name = get_current_name(name, specific_version) + + (rc, stdout, stderr) = execute_command("%s %s" % (upgrade_cmd, name), syslogging) + + # 'pkg_add -u' returns 0 even when something strange happened, stdout + # should be empty if everything went fine. + if stdout: + rc=1 + + post_upgrade_name = get_current_name(name, specific_version) + + if pre_upgrade_name == post_upgrade_name: + changed = False + else: + changed = True + + return (rc, stdout, stderr, changed) + + else: + # If package was not installed at all just make it present. + return package_present(name, installed_state) + +# Function used to make sure a package is not installed. +def package_absent(name, installed_state): + remove_cmd = 'pkg_delete -I' + if installed_state is True: + rc, stdout, stderr = execute_command("%s %s" % (remove_cmd, name), syslogging) + + if rc == 0: + changed=True + else: + changed=False + else: + rc = 0 + stdout = '' + stderr = '' + changed=False + + return (rc, stdout, stderr, changed) + +# =========================================== +# Main control flow + +def main(): + module = AnsibleModule( + argument_spec = dict( + name = dict(required=True), + state = dict(required=True, choices=['absent', 'installed', 'latest', 'present', 'removed']), + ) + ) + + name = module.params['name'] + state = module.params['state'] + + rc = 0 + stdout = '' + stderr = '' + result = {} + result['name'] = name + result['state'] = state + + # Decide if the name contains a version number. + # This regex is based on packages-specs(7). + match = re.search("-[0-9]", name) + if match: + specific_version = True + else: + specific_version = False + + # Get package state + installed_state = get_package_state(name, specific_version) + + # Perform requested action + if state in ['installed', 'present']: + (rc, stdout, stderr, changed) = package_present(name, installed_state) + elif state in ['absent', 'removed']: + (rc, stdout, stderr, changed) = package_absent(name, installed_state) + elif state == 'latest': + (rc, stdout, stderr, changed) = package_latest(name, installed_state, specific_version) + + if rc != 0: + if stderr: + module.fail_json(msg=stderr) + else: + module.fail_json(msg=stdout) + + result['changed'] = changed + + module.exit_json(**result) + +# this is magic, see lib/ansible/module_common.py +#<> +main()