summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/general/plugins/modules/urpmi.py
blob: 34e099e4d50a926a47b3121bf40e9d3e442915f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (c) 2013, Philippe Makowski
# Written by Philippe Makowski <philippem@mageia.org>
# Based on apt module written by Matthew Williams <matthew@flowroute.com>

# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

from __future__ import absolute_import, division, print_function
__metaclass__ = type

DOCUMENTATION = '''
---
module: urpmi
short_description: Urpmi manager
description:
  - Manages packages with I(urpmi) (such as for Mageia or Mandriva)
extends_documentation_fragment:
  - community.general.attributes
attributes:
  check_mode:
    support: none
  diff_mode:
    support: none
options:
  name:
    description:
      - A list of package names to install, upgrade or remove.
    required: true
    aliases: [ package, pkg ]
    type: list
    elements: str
  state:
    description:
      - Indicates the desired package state.
    choices: [ absent, present, installed, removed ]
    default: present
    type: str
  update_cache:
    description:
      - Update the package database first C(urpmi.update -a).
    type: bool
    default: false
  no_recommends:
    description:
      - Corresponds to the C(--no-recommends) option for I(urpmi).
    type: bool
    default: true
  force:
    description:
      - Assume "yes" is the answer to any question urpmi has to ask.
        Corresponds to the C(--force) option for I(urpmi).
    type: bool
    default: true
  root:
    description:
      - Specifies an alternative install root, relative to which all packages will be installed.
        Corresponds to the C(--root) option for I(urpmi).
    aliases: [ installroot ]
    type: str
author:
- Philippe Makowski (@pmakowski)
'''

EXAMPLES = '''
- name: Install package foo
  community.general.urpmi:
    pkg: foo
    state: present

- name: Remove package foo
  community.general.urpmi:
    pkg: foo
    state: absent

- name: Remove packages foo and bar
  community.general.urpmi:
    pkg: foo,bar
    state: absent

- name: Update the package database (urpmi.update -a -q) and install bar (bar will be the updated if a newer version exists)
- community.general.urpmi:
    name: bar
    state: present
    update_cache: true
'''


from ansible.module_utils.basic import AnsibleModule


def query_package(module, name, root):
    # rpm -q returns 0 if the package is installed,
    # 1 if it is not installed
    rpm_path = module.get_bin_path("rpm", True)
    cmd = "%s -q %s %s" % (rpm_path, name, root_option(root))
    rc, stdout, stderr = module.run_command(cmd, check_rc=False)
    if rc == 0:
        return True
    else:
        return False


def query_package_provides(module, name, root):
    # rpm -q returns 0 if the package is installed,
    # 1 if it is not installed
    rpm_path = module.get_bin_path("rpm", True)
    cmd = "%s -q --whatprovides %s %s" % (rpm_path, name, root_option(root))
    rc, stdout, stderr = module.run_command(cmd, check_rc=False)
    return rc == 0


def update_package_db(module):

    urpmiupdate_path = module.get_bin_path("urpmi.update", True)
    cmd = "%s -a -q" % (urpmiupdate_path,)
    rc, stdout, stderr = module.run_command(cmd, check_rc=False)
    if rc != 0:
        module.fail_json(msg="could not update package db")


def remove_packages(module, packages, root):

    remove_c = 0
    # Using a for loop in case 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, package, root):
            continue

        urpme_path = module.get_bin_path("urpme", True)
        cmd = "%s --auto %s %s" % (urpme_path, root_option(root), package)
        rc, stdout, stderr = module.run_command(cmd, check_rc=False)

        if rc != 0:
            module.fail_json(msg="failed to remove %s" % (package))

        remove_c += 1

    if remove_c > 0:

        module.exit_json(changed=True, msg="removed %s package(s)" % remove_c)

    module.exit_json(changed=False, msg="package(s) already absent")


def install_packages(module, pkgspec, root, force=True, no_recommends=True):

    packages = ""
    for package in pkgspec:
        if not query_package_provides(module, package, root):
            packages += "'%s' " % package

    if len(packages) != 0:
        if no_recommends:
            no_recommends_yes = '--no-recommends'
        else:
            no_recommends_yes = ''

        if force:
            force_yes = '--force'
        else:
            force_yes = ''

        urpmi_path = module.get_bin_path("urpmi", True)
        cmd = ("%s --auto %s --quiet %s %s %s" % (urpmi_path, force_yes,
                                                  no_recommends_yes,
                                                  root_option(root),
                                                  packages))

        rc, out, err = module.run_command(cmd)

        for package in pkgspec:
            if not query_package_provides(module, package, root):
                module.fail_json(msg="'urpmi %s' failed: %s" % (package, err))

        # urpmi always have 0 for exit code if --force is used
        if rc:
            module.fail_json(msg="'urpmi %s' failed: %s" % (packages, err))
        else:
            module.exit_json(changed=True, msg="%s present(s)" % packages)
    else:
        module.exit_json(changed=False)


def root_option(root):
    if (root):
        return "--root=%s" % (root)
    else:
        return ""


def main():
    module = AnsibleModule(
        argument_spec=dict(
            state=dict(type='str', default='present',
                       choices=['absent', 'installed', 'present', 'removed']),
            update_cache=dict(type='bool', default=False),
            force=dict(type='bool', default=True),
            no_recommends=dict(type='bool', default=True),
            name=dict(type='list', elements='str', required=True, aliases=['package', 'pkg']),
            root=dict(type='str', aliases=['installroot']),
        ),
    )

    p = module.params

    if p['update_cache']:
        update_package_db(module)

    if p['state'] in ['installed', 'present']:
        install_packages(module, p['name'], p['root'], p['force'], p['no_recommends'])

    elif p['state'] in ['removed', 'absent']:
        remove_packages(module, p['name'], p['root'])


if __name__ == '__main__':
    main()