From 88b7dc344cec69a09f2596f9cad1778015c8e01d Mon Sep 17 00:00:00 2001 From: Jakub Jirutka Date: Wed, 22 Jan 2014 20:10:53 +0100 Subject: [PATCH] Introduce module layman --- library/packaging/layman | 236 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 library/packaging/layman diff --git a/library/packaging/layman b/library/packaging/layman new file mode 100644 index 0000000000..0f7b986d49 --- /dev/null +++ b/library/packaging/layman @@ -0,0 +1,236 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2014, Jakub Jirutka +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# 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. +# +# Ansible is distributed in the hope that it will be useful, +# 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 +# along with Ansible. If not, see . + +import shutil +from os import path +from urllib2 import Request, urlopen, URLError + +DOCUMENTATION = ''' +--- +module: layman +author: Jakub Jirutka +version_added: "1.4.5" +short_description: Manage Gentoo overlays +description: + - Uses Layman to manage an additional repositories for the Portage package manager on Gentoo Linux. + Please note that Layman must be installed on a managed node prior using this module. +options: + name: + description: + - The overlay id to install, synchronize, or uninstall. + Use 'ALL' to sync all of the installed overlays (can be used only when C(state=updated)). + required: true + list_url: + description: + - An URL of the alternative overlays list that defines the overlay to install. + This list will be fetched and saved under C(${overlay_defs})/${name}.xml), where + C(overlay_defs) is readed from the Layman's configuration. + required: false + state: + description: + - Whether to install (C(present)), sync (C(updated)), or uninstall (C(absent)) the overlay. + required: false + default: present + choices: [present, absent, updated] +''' + +EXAMPLES = ''' +# Install the overlay 'mozilla' which is on the central overlays list. +- layman: name=mozilla + +# Install the overlay 'cvut' from the specified alternative list. +- layman: name=cvut list_url=http://raw.github.com/cvut/gentoo-overlay/master/overlay.xml + +# Update (sync) the overlay 'cvut', or install if not installed yet. +- layman: name=cvut list_url=http://raw.github.com/cvut/gentoo-overlay/master/overlay.xml state=updated + +# Update (sync) all of the installed overlays. +- layman: name=ALL state=updated + +# Uninstall the overlay 'cvut'. +- layman: name=cvut state=absent +''' + +USERAGENT = 'ansible-httpget' + +try: + from layman.api import LaymanAPI + from layman.config import BareConfig + HAS_LAYMAN_API = True +except ImportError: + HAS_LAYMAN_API = False + + +class ModuleError(Exception): pass + + +def init_layman(config=None): + '''Returns the initialized ``LaymanAPI``. + + :param config: the layman's configuration to use (optional) + ''' + if config is None: config = BareConfig(read_configfile=True, quietness=1) + return LaymanAPI(config) + + +def download_url(url, dest): + ''' + :param url: the URL to download + :param dest: the absolute path of where to save the downloaded content to; + it must be writable and not a directory + + :raises ModuleError + ''' + request = Request(url) + request.add_header('User-agent', USERAGENT) + + try: + response = urlopen(request) + except URLError, e: + raise ModuleError("Failed to get %s: %s" % (url, str(e))) + + try: + with open(dest, 'w') as f: + shutil.copyfileobj(response, f) + except IOError, e: + raise ModuleError("Failed to write: %s" % str(e)) + + +def install_overlay(name, list_url=None): + '''Installs the overlay repository. If not on the central overlays list, + then :list_url of an alternative list must be provided. The list will be + fetched and saved under ``%(overlay_defs)/%(name.xml)`` (location of the + ``overlay_defs`` is read from the Layman's configuration). + + :param name: the overlay id + :param list_url: the URL of the remote repositories list to look for the overlay + definition (optional, default: None) + + :returns: True if the overlay was installed, or False if already exists + (i.e. nothing has changed) + :raises ModuleError + ''' + # read Layman configuration + layman_conf = BareConfig(read_configfile=True) + layman = init_layman(layman_conf) + + if layman.is_installed(name): + return False + + if not layman.is_repo(name): + if not list_url: raise ModuleError("Overlay '%s' is not on the list of known " \ + "overlays and URL of the remote list was not provided." % name) + + overlay_defs = layman_conf.get_option('overlay_defs') + dest = path.join(overlay_defs, name + '.xml') + + download_url(list_url, dest) + + # reload config + layman = init_layman() + + if not layman.add_repos(name): raise ModuleError(layman.get_errors()) + + return True + + +def uninstall_overlay(name): + '''Uninstalls the given overlay repository from the system. + + :param name: the overlay id to uninstall + + :returns: True if the overlay was uninstalled, or False if doesn't exist + (i.e. nothing has changed) + :raises ModuleError + ''' + layman = init_layman() + + if not layman.is_installed(name): + return False + + layman.delete_repos(name) + if layman.get_errors(): raise ModuleError(layman.get_errors()) + + return True + + +def sync_overlay(name): + '''Synchronizes the specified overlay repository. + + :param name: the overlay repository id to sync + :raises ModuleError + ''' + layman = init_layman() + + if not layman.sync(name): + messages = [ str(item[1]) for item in layman.sync_results[2] ] + raise ModuleError(messages) + + +def sync_overlays(): + '''Synchronize all of the installed overlays. + + :raises ModuleError + ''' + layman = init_layman() + + for name in layman.get_installed(): + sync_overlay(name) + + +def main(): + # define module + module = AnsibleModule( + argument_spec = { + 'name': { 'required': True }, + 'list_url': { 'aliases': ['url'] }, + 'state': { 'default': "present", 'choices': ['present', 'absent', 'updated'] }, + } + ) + + if not HAS_LAYMAN_API: + module.fail_json(msg='Layman is not installed') + + state, name, url = (module.params[key] for key in ['state', 'name', 'list_url']) + + changed = False + try: + if state == 'present': + changed = install_overlay(name, url) + + elif state == 'updated': + if name == 'ALL': + sync_overlays() + elif install_overlay(name, url): + changed = True + else: + sync_overlay(name) + else: + changed = uninstall_overlay(name) + + except ModuleError, e: + module.fail_json(msg=e.message) + else: + module.exit_json(changed=changed, name=name) + + +# import module snippets +from ansible.module_utils.basic import * +main()