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
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import os
import re
import shlex
import sqlite3
DOCUMENTATION = '''
---
@ -29,6 +31,7 @@ version_added: "1.1"
short_description: Manage packages on OpenBSD.
description:
- Manage packages on OpenBSD using the pkg tools.
requirements: [ "python >= 2.5" ]
options:
name:
required: true
@ -41,6 +44,23 @@ options:
- C(present) will make sure 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.
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 = '''
@ -53,6 +73,9 @@ EXAMPLES = '''
# Make sure nmap is not installed
- 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 '--'
- 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.
def package_present(name, installed_state, pkg_spec, module):
build = module.params['build']
if module.check_mode:
install_cmd = 'pkg_add -Imn'
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:
# 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
# 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
# a package is found and 1 when it is not, if a version is not
# 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.
module.debug("package_present(): depending on return code")
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.
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:
upgrade_cmd = 'pkg_add -umn'
else:
@ -275,6 +320,7 @@ def parse_package_name(name, pkg_spec, module):
pkg_spec['version'] = match.group('version')
pkg_spec['flavor_separator'] = match.group('flavor_separator')
pkg_spec['flavor'] = match.group('flavor')
pkg_spec['style'] = 'version'
else:
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['flavor_separator'] = '-'
pkg_spec['flavor'] = match.group('flavor')
pkg_spec['style'] = 'versionless'
else:
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['flavor_separator'] = None
pkg_spec['flavor'] = None
pkg_spec['style'] = 'stem'
else:
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:
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.
def upgrade_packages(module):
if module.check_mode:
@ -348,12 +438,16 @@ def main():
argument_spec = dict(
name = dict(required=True),
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
)
name = module.params['name']
state = module.params['state']
build = module.params['build']
ports_dir = module.params['ports_dir']
rc = 0
stdout = ''
@ -361,6 +455,18 @@ def main():
result = {}
result['name'] = name
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 state != 'latest':