xbps.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. #!/usr/bin/python2.7 -tt
  2. # -*- coding: utf-8 -*-
  3. import glob
  4. import os
  5. import re
  6. import time
  7. def update_package_db(module, paths, cache_valid_time):
  8. cmd = "%s -L" % paths['query']
  9. rc, sout, serr = module.run_command(cmd, check_rc=False)
  10. # all repo indexes not older than cache_valid_time seconds?
  11. cache_valid = False
  12. if cache_valid_time:
  13. cache_valid = True
  14. matcher = re.compile('(http[^ ]+)')
  15. now = time.time()
  16. for line in sout:
  17. repo = matcher.search(line)
  18. for repofile in glob.glob('/var/db/xbps/%s/*-repodata' % repo):
  19. ctime = os.stat(repofile).st_ctime
  20. if ctime:
  21. if ctime + cache_valid_time < now:
  22. cache_valid = False
  23. if not cache_valid:
  24. cmd = "%s -S" % paths['install']
  25. rc, sout, serr = module.run_command(cmd, check_rc=False)
  26. if rc == 0:
  27. return True
  28. else:
  29. module.fail_json(msg="could not update package db", rc=rc, stdout=sout, stderr=serr)
  30. else:
  31. return False
  32. def query_package(module, paths, pkg):
  33. '''
  34. pkg: a package name
  35. returns: (installed, available, latest) as booleans
  36. '''
  37. query_local = '%s -S %s | grep "^pkgver: "' % (paths['query'], pkg)
  38. query_repo = '%s -RS %s | grep "^pkgver: "' % (paths['query'], pkg)
  39. # query local installed packages
  40. qlrc, qlsout, qlserr = module.run_command(query_local, use_unsafe_shell=True, check_rc=False)
  41. if qlrc == 0:
  42. # pkg installed
  43. installed = True
  44. else:
  45. installed = False
  46. # query repo conent
  47. qrrc, qrsout, qrserr = module.run_command(query_repo, use_unsafe_shell=True, check_rc=False)
  48. if qrrc == 0:
  49. # pkg available
  50. available = True
  51. else:
  52. available = False
  53. if qlsout == qrsout:
  54. # same pkgver
  55. latest = True
  56. else:
  57. latest = False
  58. #FIXME module.fail_json(msg='foo', ql=query_local, qlrc=qlrc, qlsout=qlsout, qlserr=qlserr, qr=query_repo, qrrc=qrrc, qrsout=qrsout, qrserr=qrserr)
  59. return (installed, available, latest)
  60. def get_changed_packages(module, paths, pkgs, desired_state):
  61. '''
  62. pkgs: list of packages
  63. desired_state: desired state (present, latest, absent)
  64. returns: list of packages to be changed
  65. '''
  66. to_be_changed = []
  67. for pkg in pkgs:
  68. (installed, available, latest) = query_package(module, paths, pkg)
  69. #FIXME module.fail_json(msg='foo', installed=installed, available=available, latest=latest)
  70. if desired_state == 'latest' and not latest:
  71. to_be_changed.append(pkg)
  72. elif desired_state == 'present' and not installed:
  73. to_be_changed.append(pkg)
  74. elif desired_state == 'absent' and installed:
  75. to_be_changed.append(pkg)
  76. return to_be_changed
  77. def check_packages(module, paths, pkgs, desired_state):
  78. '''
  79. pkgs: list of packages
  80. desired_state: desired state (present, latest, absent)
  81. retuns: n/a, exit module
  82. '''
  83. to_be_changed = get_changed_packages(module, paths, pkgs, desired_state)
  84. if to_be_changed:
  85. module.exit_json(changed=True, msg="%s package(s) not in state %s" % (
  86. len(to_be_changed), state))
  87. else:
  88. module.exit_json(changed=False, msg="package(s) already in state %s" % state)
  89. def upgrade(module, paths, check_mode):
  90. '''
  91. returns: (updates_available, updates_successful)
  92. '''
  93. # Check for updates
  94. cmd = "%s -un" % paths['install']
  95. rc, sout, serr = module.run_command(cmd, check_rc=False)
  96. if sout:
  97. # packages for upgrade available
  98. if check_mode:
  99. return (True, False)
  100. # do upgrade
  101. cmd = "%s -uy" % paths['install']
  102. rc, sout, serr = module.run_command(cmd, check_rc=False)
  103. if rc == 0:
  104. return (True, True)
  105. else:
  106. module.fail_json(msg="could not upgrade packages", rc=rc, stdout=sout, stderr=serr)
  107. else:
  108. return (False, False)
  109. def ensure_packages_state(module, paths, pkgs, desired_state):
  110. '''
  111. pkgs: list of packages
  112. desired_state: desired state (present, latest, absent)
  113. returns: list of packages to be changed
  114. '''
  115. # which packages must change state
  116. pkgs2do = get_changed_packages(module, paths, pkgs, desired_state)
  117. if not pkgs2do:
  118. module.exit_json(msg='All packages in desired state')
  119. additional_options = []
  120. if desired_state in ['present', 'latest']:
  121. cmdtype = 'install'
  122. elif desired_state == 'absent':
  123. cmdtype = 'remove'
  124. if module.params['recurse']:
  125. additional_options.append('-R')
  126. cmd = '%s %s -y %s' % (paths[cmdtype], ' '.join(additional_options), ' '.join(pkgs2do))
  127. rc, sout, serr = module.run_command(cmd, check_rc=False)
  128. if rc == 0:
  129. msg = '%s package' % len(pkgs2do)
  130. msg += '%s ' % len(pkgs2do) == 1 and '' or 's'
  131. if cmdtype == 'install':
  132. msg += 'installed'
  133. elif cmdtype == 'remove':
  134. msg += 'removed'
  135. module.exit_json(msg=msg, rc=rc, stdout=sout, stderr=serr)
  136. else:
  137. module.fail_json(msg="could not install/remove package(s)", rc=rc, stdout=sout, stderr=serr)
  138. def main():
  139. module = AnsibleModule(
  140. argument_spec = dict(
  141. name = dict(aliases=['pkg', 'package'], type='list'),
  142. state = dict(default='present', choices=['present', 'installed', "latest", 'absent', 'removed']),
  143. force = dict(default=False, type='bool'),
  144. recurse = dict(default=False, type='bool'),
  145. upgrade = dict(default=False, type='bool'),
  146. update_cache = dict(default=False, aliases=['update-cache'], type='bool'),
  147. cache_valid_time = dict(type='int'),
  148. ),
  149. required_one_of = [['name', 'update_cache', 'upgrade']],
  150. supports_check_mode = True)
  151. paths = {}
  152. paths['install'] = module.get_bin_path('xbps-install', True)
  153. paths['remove'] = module.get_bin_path('xbps-remove', True)
  154. paths['query'] = module.get_bin_path('xbps-query', True)
  155. p = module.params
  156. # normalize the state parameter
  157. if p['state'] in ['present', 'installed']:
  158. p['state'] = 'present'
  159. elif p['state'] in ['absent', 'removed']:
  160. p['state'] = 'absent'
  161. # do update if package name is "*"
  162. if p['name'] and p['name'][0] == '*' and p['state'] == 'latest':
  163. p['update'] = True
  164. p['name'] = []
  165. # sync repo index
  166. if p["update_cache"] and not module.check_mode:
  167. updated = update_package_db(module, paths, p.get('cache_valid_time'))
  168. if not (p['name'] or p['upgrade']):
  169. if updated:
  170. module.exit_json(changed=True, msg='Updated the package master lists')
  171. else:
  172. module.exit_json(changed=False, msg='Package master lists uptodate')
  173. if p['update_cache'] and module.check_mode and not (p['name'] or p['upgrade']):
  174. module.exit_json(changed=True, msg='Would have updated the package cache')
  175. # update/upgrade all installed packages
  176. if p['upgrade']:
  177. (upg_avail, upg_successful) = upgrade(module, paths, module.check_mode)
  178. if not p['name']: # upgrade is last action
  179. if upg_avail:
  180. if upg_successful:
  181. module.exit_json(changed=True, msg="all packages upgraded")
  182. else:
  183. if module.check_mode:
  184. module.exit_json(msg="no packages upgraded in check mode")
  185. # should never happen, "upgrade" function should exit module
  186. module.fail_json(msg="could not upgrade packages", rc=rc, stdout=sout, stderr=serr)
  187. else:
  188. module.exit_json(msg="all packages uptodate")
  189. if p['name']:
  190. if module.check_mode:
  191. check_packages(module, paths, p['name'], p['state']) # FIXME
  192. ensure_packages_state(module, paths, p['name'], p['state']) # FIXME
  193. # import module snippets
  194. from ansible.module_utils.basic import *
  195. if __name__ == "__main__":
  196. main()