1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3 4# Copyright: (c) 2013, Philippe Makowski 5# Written by Philippe Makowski <philippem@mageia.org> 6# Based on apt module written by Matthew Williams <matthew@flowroute.com> 7 8# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 9 10from __future__ import absolute_import, division, print_function 11__metaclass__ = type 12 13DOCUMENTATION = ''' 14--- 15module: urpmi 16short_description: Urpmi manager 17description: 18 - Manages packages with I(urpmi) (such as for Mageia or Mandriva) 19options: 20 name: 21 description: 22 - A list of package names to install, upgrade or remove. 23 required: yes 24 aliases: [ package, pkg ] 25 type: list 26 elements: str 27 state: 28 description: 29 - Indicates the desired package state. 30 choices: [ absent, present, installed, removed ] 31 default: present 32 type: str 33 update_cache: 34 description: 35 - Update the package database first C(urpmi.update -a). 36 - Alias C(update-cache) has been deprecated and will be removed in community.general 5.0.0. 37 type: bool 38 default: no 39 aliases: ['update-cache'] 40 no_recommends: 41 description: 42 - Corresponds to the C(--no-recommends) option for I(urpmi). 43 - Alias C(no-recommends) has been deprecated and will be removed in community.general 5.0.0. 44 type: bool 45 default: yes 46 aliases: ['no-recommends'] 47 force: 48 description: 49 - Assume "yes" is the answer to any question urpmi has to ask. 50 Corresponds to the C(--force) option for I(urpmi). 51 type: bool 52 default: yes 53 root: 54 description: 55 - Specifies an alternative install root, relative to which all packages will be installed. 56 Corresponds to the C(--root) option for I(urpmi). 57 aliases: [ installroot ] 58 type: str 59author: 60- Philippe Makowski (@pmakowski) 61''' 62 63EXAMPLES = ''' 64- name: Install package foo 65 community.general.urpmi: 66 pkg: foo 67 state: present 68 69- name: Remove package foo 70 community.general.urpmi: 71 pkg: foo 72 state: absent 73 74- name: Remove packages foo and bar 75 community.general.urpmi: 76 pkg: foo,bar 77 state: absent 78 79- name: Update the package database (urpmi.update -a -q) and install bar (bar will be the updated if a newer version exists) 80- community.general.urpmi: 81 name: bar 82 state: present 83 update_cache: yes 84''' 85 86 87import os 88import shlex 89import sys 90 91from ansible.module_utils.basic import AnsibleModule 92 93 94def query_package(module, name, root): 95 # rpm -q returns 0 if the package is installed, 96 # 1 if it is not installed 97 rpm_path = module.get_bin_path("rpm", True) 98 cmd = "%s -q %s %s" % (rpm_path, name, root_option(root)) 99 rc, stdout, stderr = module.run_command(cmd, check_rc=False) 100 if rc == 0: 101 return True 102 else: 103 return False 104 105 106def query_package_provides(module, name, root): 107 # rpm -q returns 0 if the package is installed, 108 # 1 if it is not installed 109 rpm_path = module.get_bin_path("rpm", True) 110 cmd = "%s -q --whatprovides %s %s" % (rpm_path, name, root_option(root)) 111 rc, stdout, stderr = module.run_command(cmd, check_rc=False) 112 return rc == 0 113 114 115def update_package_db(module): 116 117 urpmiupdate_path = module.get_bin_path("urpmi.update", True) 118 cmd = "%s -a -q" % (urpmiupdate_path,) 119 rc, stdout, stderr = module.run_command(cmd, check_rc=False) 120 if rc != 0: 121 module.fail_json(msg="could not update package db") 122 123 124def remove_packages(module, packages, root): 125 126 remove_c = 0 127 # Using a for loop in case of error, we can report the package that failed 128 for package in packages: 129 # Query the package first, to see if we even need to remove 130 if not query_package(module, package, root): 131 continue 132 133 urpme_path = module.get_bin_path("urpme", True) 134 cmd = "%s --auto %s %s" % (urpme_path, root_option(root), package) 135 rc, stdout, stderr = module.run_command(cmd, check_rc=False) 136 137 if rc != 0: 138 module.fail_json(msg="failed to remove %s" % (package)) 139 140 remove_c += 1 141 142 if remove_c > 0: 143 144 module.exit_json(changed=True, msg="removed %s package(s)" % remove_c) 145 146 module.exit_json(changed=False, msg="package(s) already absent") 147 148 149def install_packages(module, pkgspec, root, force=True, no_recommends=True): 150 151 packages = "" 152 for package in pkgspec: 153 if not query_package_provides(module, package, root): 154 packages += "'%s' " % package 155 156 if len(packages) != 0: 157 if no_recommends: 158 no_recommends_yes = '--no-recommends' 159 else: 160 no_recommends_yes = '' 161 162 if force: 163 force_yes = '--force' 164 else: 165 force_yes = '' 166 167 urpmi_path = module.get_bin_path("urpmi", True) 168 cmd = ("%s --auto %s --quiet %s %s %s" % (urpmi_path, force_yes, 169 no_recommends_yes, 170 root_option(root), 171 packages)) 172 173 rc, out, err = module.run_command(cmd) 174 175 for package in pkgspec: 176 if not query_package_provides(module, package, root): 177 module.fail_json(msg="'urpmi %s' failed: %s" % (package, err)) 178 179 # urpmi always have 0 for exit code if --force is used 180 if rc: 181 module.fail_json(msg="'urpmi %s' failed: %s" % (packages, err)) 182 else: 183 module.exit_json(changed=True, msg="%s present(s)" % packages) 184 else: 185 module.exit_json(changed=False) 186 187 188def root_option(root): 189 if (root): 190 return "--root=%s" % (root) 191 else: 192 return "" 193 194 195def main(): 196 module = AnsibleModule( 197 argument_spec=dict( 198 state=dict(type='str', default='present', 199 choices=['absent', 'installed', 'present', 'removed']), 200 update_cache=dict( 201 type='bool', default=False, aliases=['update-cache'], 202 deprecated_aliases=[dict(name='update-cache', version='5.0.0', collection_name='community.general')]), 203 force=dict(type='bool', default=True), 204 no_recommends=dict( 205 type='bool', default=True, aliases=['no-recommends'], 206 deprecated_aliases=[dict(name='no-recommends', version='5.0.0', collection_name='community.general')]), 207 name=dict(type='list', elements='str', required=True, aliases=['package', 'pkg']), 208 root=dict(type='str', aliases=['installroot']), 209 ), 210 ) 211 212 p = module.params 213 214 if p['update_cache']: 215 update_package_db(module) 216 217 if p['state'] in ['installed', 'present']: 218 install_packages(module, p['name'], p['root'], p['force'], p['no_recommends']) 219 220 elif p['state'] in ['removed', 'absent']: 221 remove_packages(module, p['name'], p['root']) 222 223 224if __name__ == '__main__': 225 main() 226