mg
c648a48c4c
Co-authored-by: Michael Grote <michael.grote@posteo.de> Reviewed-on: mg/ansible#346 Co-authored-by: mg <michael.grote@posteo.de> Co-committed-by: mg <michael.grote@posteo.de>
268 lines
7.5 KiB
Python
268 lines
7.5 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# (c) 2014, Jakub Jirutka <jakub@jirutka.cz>
|
|
#
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
__metaclass__ = type
|
|
|
|
|
|
DOCUMENTATION = '''
|
|
---
|
|
module: layman
|
|
author: "Jakub Jirutka (@jirutka)"
|
|
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.
|
|
requirements:
|
|
- "python >= 2.6"
|
|
- layman python 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
|
|
type: str
|
|
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.
|
|
aliases: [url]
|
|
type: str
|
|
state:
|
|
description:
|
|
- Whether to install (C(present)), sync (C(updated)), or uninstall (C(absent)) the overlay.
|
|
default: present
|
|
choices: [present, absent, updated]
|
|
type: str
|
|
validate_certs:
|
|
description:
|
|
- If C(no), SSL certificates will not be validated. This should only be
|
|
set to C(no) when no other option exists. Prior to 1.9.3 the code
|
|
defaulted to C(no).
|
|
type: bool
|
|
default: yes
|
|
'''
|
|
|
|
EXAMPLES = '''
|
|
- name: Install the overlay mozilla which is on the central overlays list
|
|
community.general.layman:
|
|
name: mozilla
|
|
|
|
- name: Install the overlay cvut from the specified alternative list
|
|
community.general.layman:
|
|
name: cvut
|
|
list_url: 'http://raw.github.com/cvut/gentoo-overlay/master/overlay.xml'
|
|
|
|
- name: Update (sync) the overlay cvut or install if not installed yet
|
|
community.general.layman:
|
|
name: cvut
|
|
list_url: 'http://raw.github.com/cvut/gentoo-overlay/master/overlay.xml'
|
|
state: updated
|
|
|
|
- name: Update (sync) all of the installed overlays
|
|
community.general.layman:
|
|
name: ALL
|
|
state: updated
|
|
|
|
- name: Uninstall the overlay cvut
|
|
community.general.layman:
|
|
name: cvut
|
|
state: absent
|
|
'''
|
|
|
|
import shutil
|
|
import traceback
|
|
|
|
from os import path
|
|
|
|
LAYMAN_IMP_ERR = None
|
|
try:
|
|
from layman.api import LaymanAPI
|
|
from layman.config import BareConfig
|
|
HAS_LAYMAN_API = True
|
|
except ImportError:
|
|
LAYMAN_IMP_ERR = traceback.format_exc()
|
|
HAS_LAYMAN_API = False
|
|
|
|
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
|
|
from ansible.module_utils.urls import fetch_url
|
|
|
|
|
|
USERAGENT = 'ansible-httpget'
|
|
|
|
|
|
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(module, 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
|
|
'''
|
|
|
|
# Hack to add params in the form that fetch_url expects
|
|
module.params['http_agent'] = USERAGENT
|
|
response, info = fetch_url(module, url)
|
|
if info['status'] != 200:
|
|
raise ModuleError("Failed to get %s: %s" % (url, info['msg']))
|
|
|
|
try:
|
|
with open(dest, 'w') as f:
|
|
shutil.copyfileobj(response, f)
|
|
except IOError as e:
|
|
raise ModuleError("Failed to write: %s" % str(e))
|
|
|
|
|
|
def install_overlay(module, 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 module.check_mode:
|
|
mymsg = 'Would add layman repo \'' + name + '\''
|
|
module.exit_json(changed=True, msg=mymsg)
|
|
|
|
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(module, list_url, dest)
|
|
|
|
# reload config
|
|
layman = init_layman()
|
|
|
|
if not layman.add_repos(name):
|
|
raise ModuleError(layman.get_errors())
|
|
|
|
return True
|
|
|
|
|
|
def uninstall_overlay(module, 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
|
|
|
|
if module.check_mode:
|
|
mymsg = 'Would remove layman repo \'' + name + '\''
|
|
module.exit_json(changed=True, msg=mymsg)
|
|
|
|
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=dict(
|
|
name=dict(required=True),
|
|
list_url=dict(aliases=['url']),
|
|
state=dict(default="present", choices=['present', 'absent', 'updated']),
|
|
validate_certs=dict(required=False, default=True, type='bool'),
|
|
),
|
|
supports_check_mode=True
|
|
)
|
|
|
|
if not HAS_LAYMAN_API:
|
|
module.fail_json(msg=missing_required_lib('Layman'), exception=LAYMAN_IMP_ERR)
|
|
|
|
state, name, url = (module.params[key] for key in ['state', 'name', 'list_url'])
|
|
|
|
changed = False
|
|
try:
|
|
if state == 'present':
|
|
changed = install_overlay(module, name, url)
|
|
|
|
elif state == 'updated':
|
|
if name == 'ALL':
|
|
sync_overlays()
|
|
elif install_overlay(module, name, url):
|
|
changed = True
|
|
else:
|
|
sync_overlay(name)
|
|
else:
|
|
changed = uninstall_overlay(module, name)
|
|
|
|
except ModuleError as e:
|
|
module.fail_json(msg=e.message)
|
|
else:
|
|
module.exit_json(changed=changed, name=name)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|