Merge pull request #1201 from bonds/devel

add ports support to openbsd_pkg
This commit is contained in:
Brian Coca 2016-01-19 09:11:41 -05:00
commit ad2208c7b5

View file

@ -18,8 +18,10 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import os
import re import re
import shlex import shlex
import sqlite3
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
@ -29,6 +31,7 @@ version_added: "1.1"
short_description: Manage packages on OpenBSD. short_description: Manage packages on OpenBSD.
description: description:
- Manage packages on OpenBSD using the pkg tools. - Manage packages on OpenBSD using the pkg tools.
requirements: [ "python >= 2.5" ]
options: options:
name: name:
required: true required: true
@ -41,6 +44,23 @@ options:
- C(present) will make sure the package is installed. - C(present) will make sure the package is installed.
C(latest) will make sure the latest version of the package is installed. C(latest) will make sure the latest version of the package is installed.
C(absent) will make sure the specified package is not installed. C(absent) will make sure the specified package is not installed.
build:
required: false
choices: [ yes, no ]
default: no
description:
- Build the package from source instead of downloading and installing
a binary. Requires that the port source tree is already installed.
Automatically builds and installs the 'sqlports' package, if it is
not already installed.
version_added: "2.1"
ports_dir:
required: false
default: /usr/ports
description:
- When used in combination with the 'build' option, allows overriding
the default ports source directory.
version_added: "2.1"
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -53,6 +73,9 @@ EXAMPLES = '''
# Make sure nmap is not installed # Make sure nmap is not installed
- openbsd_pkg: name=nmap state=absent - openbsd_pkg: name=nmap state=absent
# Make sure nmap is installed, build it from source if it is not
- openbsd_pkg: name=nmap state=present build=yes
# Specify a pkg flavour with '--' # Specify a pkg flavour with '--'
- openbsd_pkg: name=vim--nox11 state=present - openbsd_pkg: name=vim--nox11 state=present
@ -118,15 +141,33 @@ def get_package_state(name, pkg_spec, module):
# Function used to make sure a package is present. # Function used to make sure a package is present.
def package_present(name, installed_state, pkg_spec, module): def package_present(name, installed_state, pkg_spec, module):
build = module.params['build']
if module.check_mode: if module.check_mode:
install_cmd = 'pkg_add -Imn' install_cmd = 'pkg_add -Imn'
else: else:
install_cmd = 'pkg_add -Im' if build is True:
port_dir = "%s/%s" % (module.params['ports_dir'], get_package_source_path(name, pkg_spec, module))
if os.path.isdir(port_dir):
if pkg_spec['flavor']:
flavors = pkg_spec['flavor'].replace('-', ' ')
install_cmd = "cd %s && make clean=depends && FLAVOR=\"%s\" make install && make clean=depends" % (port_dir, flavors)
elif pkg_spec['subpackage']:
install_cmd = "cd %s && make clean=depends && SUBPACKAGE=\"%s\" make install && make clean=depends" % (port_dir, pkg_spec['subpackage'])
else:
install_cmd = "cd %s && make install && make clean=depends" % (port_dir)
else:
module.fail_json(msg="the port source directory %s does not exist" % (port_dir))
else:
install_cmd = 'pkg_add -Im'
if installed_state is False: if installed_state is False:
# Attempt to install the package # Attempt to install the package
(rc, stdout, stderr) = execute_command("%s %s" % (install_cmd, name), module) if build is True and not module.check_mode:
(rc, stdout, stderr) = module.run_command(install_cmd, module, use_unsafe_shell=True)
else:
(rc, stdout, stderr) = execute_command("%s %s" % (install_cmd, name), module)
# The behaviour of pkg_add is a bit different depending on if a # The behaviour of pkg_add is a bit different depending on if a
# specific version is supplied or not. # specific version is supplied or not.
@ -134,7 +175,7 @@ def package_present(name, installed_state, pkg_spec, module):
# When a specific version is supplied the return code will be 0 when # When a specific version is supplied the return code will be 0 when
# a package is found and 1 when it is not, if a version is not # a package is found and 1 when it is not, if a version is not
# supplied the tool will exit 0 in both cases: # supplied the tool will exit 0 in both cases:
if pkg_spec['version']: if pkg_spec['version'] or build is True:
# Depend on the return code. # Depend on the return code.
module.debug("package_present(): depending on return code") module.debug("package_present(): depending on return code")
if rc: if rc:
@ -177,6 +218,10 @@ def package_present(name, installed_state, pkg_spec, module):
# Function used to make sure a package is the latest available version. # Function used to make sure a package is the latest available version.
def package_latest(name, installed_state, pkg_spec, module): def package_latest(name, installed_state, pkg_spec, module):
if module.params['build'] is True:
module.fail_json(msg="the combination of build=%s and state=latest is not supported" % module.params['build'])
if module.check_mode: if module.check_mode:
upgrade_cmd = 'pkg_add -umn' upgrade_cmd = 'pkg_add -umn'
else: else:
@ -275,6 +320,7 @@ def parse_package_name(name, pkg_spec, module):
pkg_spec['version'] = match.group('version') pkg_spec['version'] = match.group('version')
pkg_spec['flavor_separator'] = match.group('flavor_separator') pkg_spec['flavor_separator'] = match.group('flavor_separator')
pkg_spec['flavor'] = match.group('flavor') pkg_spec['flavor'] = match.group('flavor')
pkg_spec['style'] = 'version'
else: else:
module.fail_json(msg="Unable to parse package name at version_match: " + name) module.fail_json(msg="Unable to parse package name at version_match: " + name)
@ -287,6 +333,7 @@ def parse_package_name(name, pkg_spec, module):
pkg_spec['version'] = None pkg_spec['version'] = None
pkg_spec['flavor_separator'] = '-' pkg_spec['flavor_separator'] = '-'
pkg_spec['flavor'] = match.group('flavor') pkg_spec['flavor'] = match.group('flavor')
pkg_spec['style'] = 'versionless'
else: else:
module.fail_json(msg="Unable to parse package name at versionless_match: " + name) module.fail_json(msg="Unable to parse package name at versionless_match: " + name)
@ -299,6 +346,7 @@ def parse_package_name(name, pkg_spec, module):
pkg_spec['version'] = None pkg_spec['version'] = None
pkg_spec['flavor_separator'] = None pkg_spec['flavor_separator'] = None
pkg_spec['flavor'] = None pkg_spec['flavor'] = None
pkg_spec['style'] = 'stem'
else: else:
module.fail_json(msg="Unable to parse package name at else: " + name) module.fail_json(msg="Unable to parse package name at else: " + name)
@ -309,6 +357,48 @@ def parse_package_name(name, pkg_spec, module):
if match: if match:
module.fail_json(msg="Trailing dash in flavor: " + pkg_spec['flavor']) module.fail_json(msg="Trailing dash in flavor: " + pkg_spec['flavor'])
# Function used for figuring out the port path.
def get_package_source_path(name, pkg_spec, module):
pkg_spec['subpackage'] = None
if pkg_spec['stem'] == 'sqlports':
return 'databases/sqlports'
else:
# try for an exact match first
conn = sqlite3.connect('/usr/local/share/sqlports')
first_part_of_query = 'SELECT fullpkgpath, fullpkgname FROM ports WHERE fullpkgname'
query = first_part_of_query + ' = ?'
cursor = conn.execute(query, (name,))
results = cursor.fetchall()
# next, try for a fuzzier match
if len(results) < 1:
looking_for = pkg_spec['stem'] + (pkg_spec['version_separator'] or '-') + (pkg_spec['version'] or '%')
query = first_part_of_query + ' LIKE ?'
if pkg_spec['flavor']:
looking_for += pkg_spec['flavor_separator'] + pkg_spec['flavor']
cursor = conn.execute(query, (looking_for,))
elif pkg_spec['style'] == 'versionless':
query += ' AND fullpkgname NOT LIKE ?'
cursor = conn.execute(query, (looking_for, "%s-%%" % looking_for,))
else:
cursor = conn.execute(query, (looking_for,))
results = cursor.fetchall()
# error if we don't find exactly 1 match
conn.close()
if len(results) < 1:
module.fail_json(msg="could not find a port by the name '%s'" % name)
if len(results) > 1:
matches = map(lambda x:x[1], results)
module.fail_json(msg="too many matches, unsure which to build: %s" % ' OR '.join(matches))
# there's exactly 1 match, so figure out the subpackage, if any, then return
fullpkgpath = results[0][0]
parts = fullpkgpath.split(',')
if len(parts) > 1 and parts[1][0] == '-':
pkg_spec['subpackage'] = parts[1]
return parts[0]
# Function used for upgrading all installed packages. # Function used for upgrading all installed packages.
def upgrade_packages(module): def upgrade_packages(module):
if module.check_mode: if module.check_mode:
@ -348,12 +438,16 @@ def main():
argument_spec = dict( argument_spec = dict(
name = dict(required=True), name = dict(required=True),
state = dict(required=True, choices=['absent', 'installed', 'latest', 'present', 'removed']), state = dict(required=True, choices=['absent', 'installed', 'latest', 'present', 'removed']),
build = dict(default='no', type='bool'),
ports_dir = dict(default='/usr/ports'),
), ),
supports_check_mode = True supports_check_mode = True
) )
name = module.params['name'] name = module.params['name']
state = module.params['state'] state = module.params['state']
build = module.params['build']
ports_dir = module.params['ports_dir']
rc = 0 rc = 0
stdout = '' stdout = ''
@ -361,6 +455,18 @@ def main():
result = {} result = {}
result['name'] = name result['name'] = name
result['state'] = state result['state'] = state
result['build'] = build
if build is True:
if not os.path.isdir(ports_dir):
module.fail_json(msg="the ports source directory %s does not exist" % (ports_dir))
# build sqlports if its not installed yet
pkg_spec = {}
parse_package_name('sqlports', pkg_spec, module)
installed_state = get_package_state(name, pkg_spec, module)
if not installed_state:
package_present('sqlports', installed_state, pkg_spec, module)
if name == '*': if name == '*':
if state != 'latest': if state != 'latest':