2012-11-24 03:00:59 +01:00
#!/usr/bin/python -tt
# -*- coding: utf-8 -*-
2014-03-28 19:56:59 +01:00
# (c) 2012, Afterburn <http://github.com/afterburn>
# (c) 2013, Aaron Bull Schaefer <aaron@elasticdog.com>
2012-11-24 03:00:59 +01:00
#
2014-03-28 19:56:59 +01:00
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
2012-11-24 03:00:59 +01:00
# 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.
#
2014-03-28 19:56:59 +01:00
# Ansible is distributed in the hope that it will be useful,
2012-11-24 03:00:59 +01:00
# 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
2014-03-28 19:56:59 +01:00
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
2012-11-24 03:00:59 +01:00
DOCUMENTATION = '''
- - -
module : pacman
2014-03-28 19:56:59 +01:00
short_description : Manage packages with I ( pacman )
2012-11-24 03:00:59 +01:00
description :
2014-03-28 19:56:59 +01:00
- Manage packages with the I ( pacman ) package manager , which is used by
Arch Linux and its variants .
2012-12-16 23:21:35 +01:00
version_added : " 1.0 "
2014-03-28 19:56:59 +01:00
author : Afterburn
notes : [ ]
requirements : [ ]
2012-11-24 03:00:59 +01:00
options :
2012-12-16 23:18:42 +01:00
name :
2012-11-24 03:00:59 +01:00
description :
2014-03-28 19:56:59 +01:00
- Name of the package to install , upgrade , or remove .
required : false
default : null
2012-11-24 03:00:59 +01:00
2012-12-16 23:18:42 +01:00
state :
description :
2014-03-28 19:56:59 +01:00
- Desired state of the package .
2012-12-16 23:18:42 +01:00
required : false
2014-03-28 19:56:59 +01:00
default : " present "
2015-01-11 05:56:05 +01:00
choices : [ " present " , " absent " , " latest " ]
2012-12-16 23:18:42 +01:00
2014-03-28 19:56:59 +01:00
recurse :
2012-12-16 23:18:42 +01:00
description :
2014-03-28 19:56:59 +01:00
- When removing a package , also remove its dependencies , provided
that they are not required by other packages and were not
explicitly installed by a user .
2012-12-16 23:18:42 +01:00
required : false
2013-03-12 13:18:12 +01:00
default : " no "
2014-03-28 19:56:59 +01:00
choices : [ " yes " , " no " ]
version_added : " 1.3 "
2012-12-16 23:18:42 +01:00
2014-03-28 19:56:59 +01:00
update_cache :
2013-07-23 15:01:13 +02:00
description :
2014-03-28 19:56:59 +01:00
- Whether or not to refresh the master package lists . This can be
run as part of a package installation or as a separate step .
2013-07-23 15:01:13 +02:00
required : false
default : " no "
2014-03-28 19:56:59 +01:00
choices : [ " yes " , " no " ]
2013-06-14 11:53:43 +02:00
'''
EXAMPLES = '''
# Install package foo
2014-03-28 19:56:59 +01:00
- pacman : name = foo state = present
2013-06-14 11:53:43 +02:00
2015-01-10 16:40:03 +01:00
# Upgrade package foo
2015-01-11 05:56:05 +01:00
- pacman : name = foo state = latest update_cache = yes
2015-01-10 16:40:03 +01:00
2014-03-28 19:56:59 +01:00
# Remove packages foo and bar
2013-06-14 11:53:43 +02:00
- pacman : name = foo , bar state = absent
2013-07-23 15:01:13 +02:00
# Recursively remove package baz
2013-08-03 20:27:11 +02:00
- pacman : name = baz state = absent recurse = yes
2013-07-23 15:01:13 +02:00
2014-03-28 19:56:59 +01:00
# Run the equivalent of "pacman -Syy" as a separate step
- pacman : update_cache = yes
2012-11-24 03:00:59 +01:00
'''
import json
import shlex
import os
2013-08-17 20:46:16 +02:00
import re
2012-11-24 03:00:59 +01:00
import sys
PACMAN_PATH = " /usr/bin/pacman "
2015-01-10 16:40:03 +01:00
def get_version ( pacman_output ) :
""" Take pacman -Qi or pacman -Si output and get the Version """
lines = pacman_output . split ( ' \n ' )
for line in lines :
if ' Version ' in line :
return line . split ( ' : ' ) [ 1 ] . strip ( )
return None
2014-03-28 19:56:59 +01:00
def query_package ( module , name , state = " present " ) :
2015-01-10 16:40:03 +01:00
""" Query the package status in both the local system and the repository. Returns a boolean to indicate if the package is installed, and a second boolean to indicate if the package is up-to-date. """
2014-03-28 19:56:59 +01:00
if state == " present " :
2015-01-10 16:40:03 +01:00
lcmd = " pacman -Qi %s " % ( name )
lrc , lstdout , lstderr = module . run_command ( lcmd , check_rc = False )
if lrc != 0 :
# package is not installed locally
return False , False
# get the version installed locally (if any)
lversion = get_version ( lstdout )
rcmd = " pacman -Si %s " % ( name )
rrc , rstdout , rstderr = module . run_command ( rcmd , check_rc = False )
# get the version in the repository
rversion = get_version ( rstdout )
if rrc == 0 :
# Return True to indicate that the package is installed locally, and the result of the version number comparison
# to determine if the package is up-to-date.
return True , ( lversion == rversion )
return False , False
2012-11-24 03:00:59 +01:00
def update_package_db ( module ) :
2014-03-12 21:44:03 +01:00
cmd = " pacman -Syy "
2014-03-10 22:11:24 +01:00
rc , stdout , stderr = module . run_command ( cmd , check_rc = False )
2012-11-24 03:00:59 +01:00
2014-03-28 19:56:59 +01:00
if rc == 0 :
return True
else :
2012-11-24 03:00:59 +01:00
module . fail_json ( msg = " could not update package db " )
2014-03-28 19:56:59 +01:00
2012-11-24 03:00:59 +01:00
def remove_packages ( module , packages ) :
2013-08-03 20:27:11 +02:00
if module . params [ " recurse " ] :
2013-07-23 15:01:13 +02:00
args = " Rs "
else :
args = " R "
2014-03-28 19:56:59 +01:00
2012-11-24 03:00:59 +01:00
remove_c = 0
# Using a for loop incase 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
2015-01-10 16:40:03 +01:00
installed , updated = query_package ( module , package )
if not installed :
2012-11-24 03:00:59 +01:00
continue
2014-03-12 21:44:03 +01:00
cmd = " pacman - %s %s --noconfirm " % ( args , package )
2014-03-10 22:11:24 +01:00
rc , stdout , stderr = module . run_command ( cmd , check_rc = False )
2012-11-24 03:00:59 +01:00
if rc != 0 :
module . fail_json ( msg = " failed to remove %s " % ( package ) )
2014-03-28 19:56:59 +01:00
2012-11-24 03:00:59 +01:00
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 " )
2015-01-11 05:56:05 +01:00
def install_packages ( module , state , packages , package_files ) :
2012-11-24 03:00:59 +01:00
install_c = 0
2013-08-17 20:46:16 +02:00
for i , package in enumerate ( packages ) :
2015-01-11 05:56:05 +01:00
# if the package is installed and state == present or state == latest and is up-to-date then skip
2015-01-10 16:40:03 +01:00
installed , updated = query_package ( module , package )
2015-01-11 05:56:05 +01:00
if installed and ( state == ' present ' or ( state == ' latest ' and updated ) ) :
2012-11-24 03:00:59 +01:00
continue
2013-08-17 20:46:16 +02:00
if package_files [ i ] :
params = ' -U %s ' % package_files [ i ]
else :
params = ' -S %s ' % package
2014-03-12 21:44:03 +01:00
cmd = " pacman %s --noconfirm " % ( params )
2014-03-10 22:11:24 +01:00
rc , stdout , stderr = module . run_command ( cmd , check_rc = False )
2012-11-24 03:00:59 +01:00
if rc != 0 :
module . fail_json ( msg = " failed to install %s " % ( package ) )
install_c + = 1
2014-03-28 19:56:59 +01:00
2012-11-24 03:00:59 +01:00
if install_c > 0 :
module . exit_json ( changed = True , msg = " installed %s package(s) " % ( install_c ) )
module . exit_json ( changed = False , msg = " package(s) already installed " )
2013-08-17 23:44:41 +02:00
def check_packages ( module , packages , state ) :
would_be_changed = [ ]
for package in packages :
2015-01-11 05:56:05 +01:00
installed , updated = query_package ( module , package )
if ( ( state in [ " present " , " latest " ] and not installed ) or
( state == " absent " and installed ) or
( state == " latest " and not updated ) ) :
2013-08-17 23:44:41 +02:00
would_be_changed . append ( package )
if would_be_changed :
if state == " absent " :
state = " removed "
module . exit_json ( changed = True , msg = " %s package(s) would be %s " % (
len ( would_be_changed ) , state ) )
else :
module . exit_json ( change = False , msg = " package(s) already %s " % state )
2012-12-16 23:18:42 +01:00
2012-11-24 03:00:59 +01:00
def main ( ) :
module = AnsibleModule (
2014-03-28 19:56:59 +01:00
argument_spec = dict (
name = dict ( aliases = [ ' pkg ' ] ) ,
2015-01-11 05:56:05 +01:00
state = dict ( default = ' present ' , choices = [ ' present ' , ' installed ' , " latest " , ' absent ' , ' removed ' ] ) ,
2014-03-28 19:56:59 +01:00
recurse = dict ( default = ' no ' , choices = BOOLEANS , type = ' bool ' ) ,
update_cache = dict ( default = ' no ' , aliases = [ ' update-cache ' ] , choices = BOOLEANS , type = ' bool ' ) ) ,
required_one_of = [ [ ' name ' , ' update_cache ' ] ] ,
supports_check_mode = True )
2012-11-24 03:00:59 +01:00
if not os . path . exists ( PACMAN_PATH ) :
module . fail_json ( msg = " cannot find pacman, looking for %s " % ( PACMAN_PATH ) )
p = module . params
2014-03-28 19:56:59 +01:00
# normalize the state parameter
if p [ ' state ' ] in [ ' present ' , ' installed ' ] :
p [ ' state ' ] = ' present '
elif p [ ' state ' ] in [ ' absent ' , ' removed ' ] :
p [ ' state ' ] = ' absent '
2013-08-17 23:44:41 +02:00
if p [ " update_cache " ] and not module . check_mode :
2012-12-16 23:18:42 +01:00
update_package_db ( module )
2014-03-28 19:56:59 +01:00
if not p [ ' name ' ] :
module . exit_json ( changed = True , msg = ' updated the package master lists ' )
2014-07-12 17:57:30 +02:00
if p [ ' update_cache ' ] and module . check_mode and not p [ ' name ' ] :
module . exit_json ( changed = True , msg = ' Would have updated the package cache ' )
2014-03-28 19:56:59 +01:00
if p [ ' name ' ] :
pkgs = p [ ' name ' ] . split ( ' , ' )
pkg_files = [ ]
for i , pkg in enumerate ( pkgs ) :
if pkg . endswith ( ' .pkg.tar.xz ' ) :
# The package given is a filename, extract the raw pkg name from
# it and store the filename
pkg_files . append ( pkg )
pkgs [ i ] = re . sub ( ' -[0-9].*$ ' , ' ' , pkgs [ i ] . split ( ' / ' ) [ - 1 ] )
else :
pkg_files . append ( None )
if module . check_mode :
check_packages ( module , pkgs , p [ ' state ' ] )
2015-01-11 05:56:05 +01:00
if p [ ' state ' ] in [ ' present ' , ' latest ' ] :
install_packages ( module , p [ ' state ' ] , pkgs , pkg_files )
2014-03-28 19:56:59 +01:00
elif p [ ' state ' ] == ' absent ' :
remove_packages ( module , pkgs )
2012-11-24 03:00:59 +01:00
2013-12-02 21:13:49 +01:00
# import module snippets
2013-12-02 21:11:23 +01:00
from ansible . module_utils . basic import *
2012-11-24 03:00:59 +01:00
main ( )