#!/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 = ''' # Make sure nmap is installed - openbsd_pkg: name=nmap state=present # Make sure nmap is the latest version - openbsd_pkg: name=nmap state=latest # Make sure nmap is not installed - 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, module): if module.check_mode: install_cmd = 'pkg_add -In' else: install_cmd = 'pkg_add -I' if installed_state is False: # Attempt to install the package (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: if module.check_mode: module.exit_json(changed=True) 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, module): if module.check_mode: upgrade_cmd = 'pkg_add -umn' else: upgrade_cmd = 'pkg_add -um' pre_upgrade_name = '' if installed_state is True: # Fetch name of currently installed package pre_upgrade_name = get_current_name(name, specific_version) # Attempt to upgrade the package (rc, stdout, stderr) = execute_command("%s %s" % (upgrade_cmd, name), syslogging) # Look for output looking something like "nmap-6.01->6.25: ok" to see if # something changed (or would have changed). Use \W to delimit the match # from progress meter output. match = re.search("\W%s->.+: ok\W" % pre_upgrade_name, stdout) if match: if module.check_mode: module.exit_json(changed=True) changed = True else: changed = False # 'pkg_add -u' returns 0 even when something strange happened, stderr # should be empty if everything went fine. if stderr: rc=1 return (rc, stdout, stderr, changed) else: # If package was not installed at all just make it present. return package_present(name, installed_state, module) # Function used to make sure a package is not installed. def package_absent(name, installed_state, module): if module.check_mode: remove_cmd = 'pkg_delete -In' else: remove_cmd = 'pkg_delete -I' if installed_state is True: # Attempt to remove the package rc, stdout, stderr = execute_command("%s %s" % (remove_cmd, name), syslogging) if rc == 0: if module.check_mode: module.exit_json(changed=True) 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']), ), supports_check_mode = True ) 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, module) elif state in ['absent', 'removed']: (rc, stdout, stderr, changed) = package_absent(name, installed_state, module) elif state == 'latest': (rc, stdout, stderr, changed) = package_latest(name, installed_state, specific_version, module) 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()