]> TLD Linux GIT Repositories - packages/ansible.git/blob - poldek.py
- use pip source, install poldek.py, no shebang in poldek.py
[packages/ansible.git] / poldek.py
1 # -*- coding: utf-8 -*-
2
3 # Copyright: (c) 2019, Marcin Krol <hawk@tld-linux.org>
4 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
6 from __future__ import absolute_import, division, print_function
7 __metaclass__ = type
8
9 ANSIBLE_METADATA = {'metadata_version': '1.0',
10                     'status': ['preview'],
11                     'supported_by': 'community'}
12
13 DOCUMENTATION = '''
14 ---
15 module: poldek
16 short_description: Manage packages with I(poldek)
17 description:
18     - Manage packages with the I(poldek) package manager, which is used by TLD Linux and PLD Linux
19 version_added: "1.0"
20 author:
21     - Marcin Krol <hawk@tld-linux.org>
22 options:
23     name:
24         description:
25             - Name or list of names of the package(s) or file(s) to install, upgrade, or remove.
26               Can't be used in combination with C(upgrade).
27         aliases: [ package, pkg ]
28
29     state:
30         description:
31             - Desired state of the package.
32         default: present
33         choices: [ absent, latest, present ]
34
35     extra_args:
36         description:
37             - Additional option to pass to poldek when enforcing C(state).
38         default:
39
40     update_cache:
41         description:
42             - Whether or not to refresh the master package lists.
43             - This can be run as part of a package installation or as a separate step.
44         default: no
45         type: bool
46         aliases: [ update-cache ]
47
48     update_cache_extra_args:
49         description:
50             - Additional option to pass to poldek when enforcing C(update_cache).
51         default:
52
53     upgrade:
54         description:
55             - Whether or not to upgrade the whole system.
56               Can't be used in combination with C(name).
57         default: no
58         type: bool
59
60     upgrade_extra_args:
61         description:
62             - Additional option to pass to poldek when enforcing C(upgrade).
63         default:
64 '''
65
66 RETURN = '''
67 packages:
68     description: a list of packages that have been changed
69     returned: when upgrade is set to yes
70     type: list
71     sample: [ package, other-package ]
72 '''
73
74 EXAMPLES = '''
75 - name: Install packages foo and bar
76   poldek:
77     name:
78       - foo
79       - bar
80     state: present
81
82 - name: Update package cache and upgrade package foo
83   poldek:
84     name: foo
85     state: latest
86     update_cache: yes
87
88 - name: Remove packages foo and bar
89   poldek:
90     name:
91       - foo
92       - bar
93     state: absent
94
95 - name: Update package cache only
96   poldek:
97     update_cache: yes
98
99 - name: Update package cache and upgrade all packages
100   poldek:
101     upgrade: yes
102     update_cache: yes
103 '''
104
105 import re
106
107 from ansible.module_utils.basic import AnsibleModule
108
109
110 def query_package(module, poldek_path, name):
111     """Query the package status in both the local system and the repository. Returns a boolean to indicate if the package is installed, a second
112     boolean to indicate if the package is up-to-date. Note: indexes must be up to date to ensure that poldek knows latest version of package.
113     """
114     lcmd = "%s -q --shcmd='ls -n --installed %s'" % (poldek_path, name)
115     lrc, lstdout, lstderr = module.run_command(lcmd, check_rc=False)
116     rcmd = "%s -q --shcmd='ls -n --installed --upgradeable %s'" % (poldek_path, name)
117     rrc, rstdout, rstderr = module.run_command(rcmd, check_rc=False)
118     if lrc != 0 or rrc != 0:
119         return False, False
120
121     pkg_latest = False
122     if rstdout == "":
123         return True, True
124     else:
125         return True, False
126
127
128 def update_package_db(module, poldek_path):
129     if module.params['force']:
130         module.params["update_cache_extra_args"] += " --upa"
131
132     cmd = "%s --noask --up %s" % (poldek_path, module.params["update_cache_extra_args"])
133     rc, stdout, stderr = module.run_command(cmd, check_rc=False)
134
135     if rc == 0:
136         return True
137     else:
138         module.fail_json(msg="error updating package database")
139
140
141 def upgrade(module, poldek_path):
142     cmdupgrade = "%s -v --noask --upgrade-dist %s" % (poldek_path, module.params["upgrade_extra_args"])
143     cmdupgradeable = "%s -q --shcmd='ls --upgradeable --qf=\"%%{NAME}-%%{VERSION}-%%{RELEASE}\n\"' %s" % (poldek_path, module.params["upgrade_extra_args"])
144     rc, stdout, stderr = module.run_command(cmdupgradeable, check_rc=False)
145
146     packages = stdout.split('\n')[:-1]
147     if stdout == "":
148         module.exit_json(changed=False, msg='Nothing to upgrade', packages=packages, stdout=stdout, stderr=stderr, rc=rc)
149     else:
150         if module.check_mode:
151             module.exit_json(changed=True, msg="%s package(s) would be upgraded" % (len(packages)), packages=packages, stdout=stdout, stderr=stderr, rc=rc)
152         rc, stdout, stderr = module.run_command(cmdupgrade, check_rc=False)
153         if rc == 0:
154             module.exit_json(changed=True, msg="%s package(s) upgraded" % (len(packages)), packages=packages, stdout=stdout, stderr=stderr, rc=rc)
155         else:
156             module.fail_json(msg="Error while upgrading packages", stdout=stdout, stderr=stderr, rc=rc)
157
158
159 def remove_packages(module, poldek_path, packages):
160     if module.params["force"]:
161         module.params["extra_args"] += " --force --nodeps"
162
163     remove_c = 0
164     out = ""
165     err = ""
166
167     for package in packages:
168         installed, updated = query_package(module, poldek_path, package)
169         if not installed:
170             continue
171
172         cmd = "%s -v --noask --erase %s %s" % (poldek_path, module.params["extra_args"], package)
173         rc, stdout, stderr = module.run_command(cmd, check_rc=False)
174
175         out = out + stdout
176         err = err + stderr
177
178         if rc != 0:
179             module.fail_json(msg="failed to remove %s" % (package), stdout=out, stderr=err, rc=rc)
180
181         remove_c += 1
182
183     if remove_c > 0:
184         module.exit_json(changed=True, msg="removed %s package(s)" % (remove_c), packages=packages, stdout=out, stderr=err, rc=0)
185
186     module.exit_json(changed=False, msg="package(s) already absent", packages=packages, stdout=out, stderr=err, rc=0)
187
188
189 def install_packages(module, poldek_path, state, packages):
190     if module.params["force"]:
191         module.params["extra_args"] += " --force --nodeps"
192
193     install_c = 0
194     out = ""
195     err = ""
196
197     for package in packages:
198         installed, updated = query_package(module, poldek_path, package)
199         if installed and (state == 'present' or (state == 'latest' and updated)):
200             continue
201
202         cmd = "%s -v --noask --upgrade %s %s" % (poldek_path, module.params["extra_args"], package)
203         rc, stdout, stderr = module.run_command(cmd, check_rc=False)
204
205         out = out + stdout
206         err = err + stderr
207
208         if rc != 0:
209             module.fail_json(msg="failed to install %s" % (package), stdout=out, stderr=err, rc=rc)
210
211         install_c += 1
212
213     if install_c > 0:
214         module.exit_json(changed=True, msg="installed %s package(s)" % (install_c), packages=packages, stdout=out, stderr=err, rc=0)
215
216     module.exit_json(changed=False, msg="package(s) already installed", packages=packages, stdout=out, stderr=err, rc=0)
217
218
219 def check_packages(module, poldek_path, packages, state):
220     would_be_changed = []
221
222     for package in packages:
223         installed, updated = query_package(module, poldek_path, package)
224         if ((state in ["present", "latest"] and not installed) or
225                 (state == "absent" and installed) or
226                 (state == "latest" and not updated)):
227             would_be_changed.append(package)
228
229     if state == "absent":
230         state = "removed"
231     if state == "present" or state == "latest":
232         state = "installed"
233
234     if would_be_changed:
235         module.exit_json(changed=True, msg="%s package(s) would be %s" % (
236             len(would_be_changed), state))
237     else:
238         module.exit_json(changed=False, msg="package(s) already %s" % state)
239
240
241 def main():
242     module = AnsibleModule(
243         argument_spec=dict(
244             name=dict(type='list', aliases=['pkg', 'package']),
245             state=dict(type='str', default='present', choices=['present', 'installed', 'latest', 'absent', 'removed']),
246             extra_args=dict(type='str', default=''),
247             upgrade=dict(type='bool', default=False),
248             upgrade_extra_args=dict(type='str', default=''),
249             update_cache=dict(type='bool', default=False, aliases=['update-cache']),
250             update_cache_extra_args=dict(type='str', default=''),
251             force=dict(type='bool', default=False),
252         ),
253         required_one_of=[['name', 'update_cache', 'upgrade']],
254         mutually_exclusive=[['name', 'upgrade']],
255         supports_check_mode=True,
256     )
257
258     poldek_path = module.get_bin_path('poldek', True)
259
260     p = module.params
261
262     if p['state'] in ['present', 'installed']:
263         p['state'] = 'present'
264     elif p['state'] in ['absent', 'removed']:
265         p['state'] = 'absent'
266
267     if p["update_cache"] and not module.check_mode:
268         update_package_db(module, poldek_path)
269         if not (p['name'] or p['upgrade']):
270             module.exit_json(changed=True, msg='Updated package database')
271
272     if p['update_cache'] and module.check_mode and not (p['name'] or p['upgrade']):
273         module.exit_json(changed=True, msg='Would have updated package database')
274
275     if p['upgrade']:
276         upgrade(module, poldek_path)
277
278     if p['name']:
279         pkgs = p['name']
280
281         if module.check_mode:
282             check_packages(module, poldek_path, pkgs, p['state'])
283
284         if p['state'] in ['present', 'latest']:
285             install_packages(module, poldek_path, p['state'], pkgs)
286         elif p['state'] == 'absent':
287             remove_packages(module, poldek_path, pkgs)
288     else:
289         module.exit_json(changed=False, msg="No package specified to work on")
290
291
292 if __name__ == "__main__":
293     main()