diff --git a/plugins/modules/univention_apps.py b/plugins/modules/univention_apps.py deleted file mode 100755 index bd11ac6..0000000 --- a/plugins/modules/univention_apps.py +++ /dev/null @@ -1,310 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2020, Univention GmbH -# Written by Lukas Zumvorde -# Based on univention_apps module written by Alexander Ulpts - -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - - -import sys -import os -import json -import tempfile -import platform -from ansible.module_utils.basic import AnsibleModule - -DOCUMENTATION = ''' ---- -module: univention_apps -version_added: "0.1.1" -short_description: "Installs and removes apps on Univention Corporate Server" -extends_documentation_fragment: '' -description: - - Allows ansible to control installation, removal and update of ucs-apps -notes: - - none -requirements: [ ] -author: Stefan Ahrens -options: - name: - description: - - 'The name of the app' - required: true - state: - description: - - 'The desired state of the app / present, or absent' - required: true - upgrade: - description: - - 'Upgrade the app if installed and upgradable' - required: false - auth_username: - description: - - 'The name of the user with witch to install apps (usually domain-admin)' - required: true - auth_password: - description: - - 'The password needed to install apps (usually domain-admin)' - required: true -''' - -EXAMPLES = ''' -- name: Install nagios - univention_apps: - name: nagios - state: present - auth_username: Administrator - auth_password: secret - -- name: remove nagios - univention_apps: - name: nagios - state: absent - auth_username: Administrator - auth_password: secret - -- name: upgrade nagios - univention_apps: - name: nagios - state: present - upgrade: True - auth_username: Administrator - auth_password: secret -''' - -RETURN = ''' -msg: - description: a return message - returned: success, failure - type: str - sample: Non-UCS-system detected. Nothing to do here. -changed: - description: if any changes were performed - returned: success - type: bool - sample: True -''' - -def check_ucs(): - ''' Check if system is actually UCS, return bool ''' - return platform.dist()[0].lower() == 'univention' - -def ansible_exec(action, appname=None, keyfile=None, username=None): - ''' runs ansible's run_command(), choose from actions install, remove, upgrade ''' - univention_app_cmd = { - 'list' : "univention-app list --ids-only", - 'info' : "univention-app info --as-json", - 'install' : "univention-app {} --noninteractive --username {} --pwdfile {} {}".format(action, username, keyfile, appname), - 'remove' : "univention-app {} --noninteractive --username {} --pwdfile {} {}".format(action, username, keyfile, appname), - 'upgrade' : "univention-app {} --noninteractive --username {} --pwdfile {} {}".format(action, username, keyfile, appname), - 'stall' : "univention-app {} {}".format(action, appname), - 'undo_stall': "univention-app {} {} --undo".format(action, appname), - } - return module.run_command(univention_app_cmd[action]) - -def get_apps_status(): - ''' Get the status of available, installed and upgradable apps and return lists''' - def get_app_list(): - ''' exec to get list of all available apps on this system ''' - return ansible_exec(action='list')[1] - def get_app_info(): - ''' exec to get lists of installed and upgradable apps on this system ''' - app_info = ansible_exec(action='info') - try: - app_infos = json.loads(app_info[1]) - except Exception as e: - module.fail_json(msg="unable to parse json: {}".format(e)) - return app_infos['installed'], app_infos['upgradable'] - - global available_apps_list - global installed_apps_list - global upgradable_apps_list - available_apps_list = get_app_list() - installed_apps_list, upgradable_apps_list = get_app_info() - -def check_app_present(_appname): - ''' check if a given app is in installed_apps_list, return bool ''' - return _appname in available_apps_list and filter(lambda x: _appname in x, installed_apps_list) - -def check_app_absent(_appname): - ''' check if a given app is NOT in installed_apps_list, return bool ''' - return _appname in available_apps_list and not filter(lambda x: _appname in x, installed_apps_list) - -def check_app_upgradeable(_appname): - ''' check if a given app is in upgradable_apps_list, return bool ''' - return _appname in available_apps_list and filter(lambda x: _appname in x, upgradable_apps_list) - -def generate_tmp_auth_file(_data): - ''' generate a temporaty auth-file and return path, MUST BE DELETED ''' - fileTemp = tempfile.NamedTemporaryFile(delete = False, mode='w') - fileTemp.write(_data) - fileTemp.close() - return fileTemp.name - -def install_app(_appname, _authfile): - ''' installs an app with given name and path to auth-file, uses ansible_exec() - and returns tuple of exit-code and stdout ''' - return ansible_exec(action='install', appname=_appname, keyfile=_authfile) - -def remove_app(_appname, _authfile): - ''' removes an app with given name and path to auth-file, uses ansible_exec() - and returns tuple of exit-code and stdout''' - return ansible_exec(action='remove', appname=_appname, keyfile=_authfile) - -def upgrade_app(_appname, _authfile): - ''' upgrades an app with given name and path to auth-file, uses ansible_exec() - and returns tuple of exit-code and stdout''' - return ansible_exec(action='upgrade', appname=_appname, keyfile=_authfile) - -def stall_app(_appname, _authfile): - ''' stalls an app with given name and path to auth-file, uses ansible_exec() and return tuple of exit-code and stdout. ''' - return ansible_exec(action='stall', appname=_appname, keyfile=_authfile) - -def undo_stall_app(_appname, _authfile): - ''' undos the stalling of an app with given name and path to auth-file, uses ansible_exec() and return tuple of exit-code and stdout. ''' - return ansible_exec(action='undo_stall', appname=_appname, keyfile=_authfile) - -def main(): - ''' main() is an entry-point for ansible which checks app-status and installs, - upgrades, or removes the app based on ansible state and name-parameters ''' - global module # declare ansible-module and parameters globally - module = AnsibleModule( - argument_spec = dict( - name = dict( - type='str', - required=True, - aliases=['app'] - ), - state = dict( - type='str', - default='present', - choices=['present', 'absent'] - ), - upgrade = dict( - type='bool', - required=False, - default=False - ), - stall = dict( - type='str', - required=False, - choices=['yes','no'] - ), - auth_password = dict( - type="str", - required=True, - no_log=True - ), - auth_username = dict( - type="str", - required=True - ), - ), - # mutually_exclusive=[[]], - # required_one_of=[[]], - supports_check_mode=False, # this has to be changed. Use -dry-run were necessary - ) - - # This module should only run on UCS-systems - if not check_ucs(): - changed = False - return module.exit_json( - changed=changed, - msg='Non-UCS-system detected. Nothing to do here.' - ) - - # gather infos and vars - get_apps_status() - app_status_target = module.params.get('state') # desired state of the app - app_status_upgrade = module.params.get('upgrade') # upgrade app if installed - app_name = module.params.get('name') # name of the app - auth_password = module.params.get('auth_password') # password for domain-adimin - # check states and explicitly check for presence and absence of app - app_present = check_app_present(app_name) - app_absent = check_app_absent(app_name) - app_upgradeable = check_app_upgradeable(app_name) - app_stall_taget = module.params.get('stall') # desired stalling state of the app - - # some basic logic-checks - if not app_absent and not app_present: # this means the app does not exist - module.fail_json(msg="app {} does not exist. Please choose from following options:\n{}".format(app_name, str(available_apps_list))) - if app_absent and app_present: # schroedinger's app-status - module.fail_json(msg="an error occured while getting the status of {}".format(app_name)) - - # upgrade, install or remove the app, or just do nothing at all and exit - if app_status_target == 'present' and app_upgradeable and app_status_upgrade: - #upgrade_app(app_name) - auth_file = generate_tmp_auth_file(auth_password) - try: - _upgrade_app = upgrade_app(app_name, auth_file) - if _upgrade_app[0] == 0: - module.exit_json(changed=True, msg="App {} successfully upgraded.".format(app_name)) - else: - module.fail_json(msg="an error occured while upgrading {}".format(app_name)) - finally: - os.remove(auth_file) - - if app_status_target == 'present' and app_present: - module.exit_json(changed=False, msg="App {} already installed. No change.".format(app_name)) - - elif app_status_target == 'present' and not app_present: - #install_app(app_name) - auth_file = generate_tmp_auth_file(auth_password) - try: - _install_app = install_app(app_name, auth_file) - if _install_app[0] == 0: - module.exit_json(changed=True, msg="App {} successfully installed.".format(app_name)) - else: - module.fail_json(msg="an error occured while installing {}".format(app_name)) - finally: - os.remove(auth_file) - - elif app_status_target == 'absent' and app_present: - #remove_app(app_name) - auth_file = generate_tmp_auth_file(auth_password) - try: - _remove_app = remove_app(app_name, auth_file) - if _remove_app[0] == 0: - module.exit_json(changed=True, msg="App {} successfully removed.".format(app_name)) - else: - module.fail_json(msg="an error occured while uninstalling {}".format(app_name)) - finally: - os.remove(auth_file) - - elif app_status_target == 'absent' and app_absent: - module.exit_json(changed=False, msg="App {} not installed. No change.".format(app_name)) - - if app_present and app_stall_target == 'yes': - #stall_app(app_name) - auth_file = generate_tmp_auth_file(auth_password) - try: - _stall_app = stall_app(app_name, auth_file) - if _stall_app[0] == 0: - module.exit_json(changed=True, msg="App {} successfully stalled.".format(app_name)) - else: - module.fail_json(msg="an error occured while stalling {}".format(app_name)) - finally: - os.remove(auth_file) - elif app_present and app_stall_target == 'no': - #undo_stall_app(app_name) - auth_file = generate_tmp_auth_file(auth_password) - try: - _undo_stall_app = undo_stall_app(app_name, auth_file) - if _undo_stall_app[0] == 0: - module.exit_json(changed=True, msg="App {} successfully unstalled.".format(app_name)) - else: - module.fail_json(msg="an error occured while undoing the stall {}".format(app_name)) - finally: - os.remove(auth_file) - elif app_present and app_stall_target and not app_stall_target in ['yes','no']: - module.fail_json(changed=False, msg="Unrecognised target state for option stall") - - else: # just in case ... - module.fail_json(msg="an unknown error occured while handling {}".format(app_name)) - - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/univention_directory_manager.py b/plugins/modules/univention_directory_manager.py deleted file mode 100755 index 6477499..0000000 --- a/plugins/modules/univention_directory_manager.py +++ /dev/null @@ -1,310 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright: (c) 2020, Univention GmbH -# Written by Lukas Zumvorde -# Based on univention_apps module written by Alexander Ulpts - -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -ANSIBLE_METADATA = { - 'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'comunity' -} - -DOCUMENTATION = ''' ---- -module: univention_directory_manager - -short_description: Accessing the Univention Directory Manager - -description: - - "You can create and modify Objects in the LDAP with Univention Directory Manager." - -options: - module: - description: - - The udm module for witch objects are to be modified - type: str - required: True - position: - description: - - The position in the tree - type: str - required: False - dn: - description: - - The distinguished name of the LDAP object. - type: str - required: false - filter: - description: - - A LDAP search filter to select objects. - required: false - state: - description: - - Either 'present' for creating or modifying the objects given with - 'dn' and 'filter' or 'absent' for deleting the objects from the LDAP. - Default is 'present'. - type: str - choices: [ absent, present ] - default: present - set_properties: - description: - - A list of dictionaries with the keys property and value. - - Properties of the objects are to be set to the given values. - type: list - required: False - unset_properties: - description: - - A list of dictionaries with the key property. - - The listed properties of the objects are to be unset. - type: list - required: False - -author: - - Lukas Zumvorde -''' - -EXAMPLES = ''' -# create a new user object -- name: create a user - univention_directory_manager: - module: 'users/user' - action: 'modify' - state: 'present' - set_properties: - - property: 'username' - value: 'testuser1' - - property: 'lastname' - value: 'testuser1' - - property: 'password' - value: 'univention' - -# delete one or more objects -- name: delete a user with a search filter - univention_directory_manager: - module: 'users/user' - action: 'modify' - state: 'absent' - filter: '(uid=testuser1)' - -# use position to place the object in the directory tree -- name: create a user with position - univention_directory_manager: - module: 'users/user' - action: 'modify' - state: 'present' - position: 'cn=users,ou=DEMOSCHOOL,dc=t1,dc=intranet' - set_properties: - - property: 'username' - value: 'testuser2' - - property: 'lastname' - value: 'testuser2' - - property: 'password' - value: 'univention' - -# delete on very specific object -- name: delete the user with position - univention_directory_manager: - module: 'users/user' - action: 'modify' - state: 'absent' - dn: 'uid=testuser2,cn=users,ou=DEMOSCHOOL,dc=t1,dc=intranet' - -# add or change specific properties -- name: modify testuser3 - add or change a property - univention_directory_manager: - module: 'users/user' - action: 'modify' - state: 'present' - filter: '(uid=testuser3)' - set_properties: - - property: 'firstname' - value: 'max' - -# remove specific properties -- name: modify testuser3 - remove property - univention_directory_manager: - module: 'users/user' - action: 'modify' - state: 'present' - filter: '(uid=testuser3)' - unset_properties: - - property: 'firstname' - value: 'does not matter' -''' - -RETURN = ''' -meta['changed_objects']: - description: A list of all objects that were changed. -message: - description: A human-readable information about which objects were changed. -''' - -import datetime -import pprint -from ansible.module_utils.basic import AnsibleModule - -try: - from univention.udm import UDM - have_udm = True -except: - have_udm = False - -class Stats(): - changed_objects = [] - -def _set_property(obj,prop,value): - setattr(obj.props,prop,value) - -def _create_or_modify_object(udm_mod,module,stats): - try: - obj = udm_mod.get(module.params['dn']) - except: - obj = None - if obj: - _modify_object(udm_mod,module,obj,stats) - else: - _create_object(udm_mod,module,stats) - -def _create_object(udm_mod,module,stats): - params = module.params - obj = udm_mod.new() - if module.params['dn']: - pass # read position and name from dn - else: - if params['position']: - obj.position = params['position'] - # TODO add policies and options - # if params['policies']: - # obj.policies = params['policies'] - # if params['options']: - # obj.options.append(params['options']) - for attr in params['set_properties']: - prop_name = attr['property'] - prop_value = attr['value'] - _set_property(obj,prop_name,prop_value) - if not module.check_mode: - obj.save() - stats.changed_objects.append(obj.dn) - -def _modify_object(udm_mod,module,obj,stats): - params = module.params - # TODO add policies and options - # if params['policies']: - # obj.policies = params['policies'] - # if params['options']: - # obj.options.append(params['options']) - if params['unset_properties']: - for attr in params['unset_properties']: - prop_name = attr['property'] - _set_property(obj,prop_name,None) - if params['set_properties']: - for attr in params['set_properties']: - prop_name = attr['property'] - prop_value = attr['value'] - _set_property(obj,prop_name,prop_value) - if not module.check_mode: - obj.save() - stats.changed_objects.append(obj.dn) - -def _remove_objects(udm_mod,module,stats): - params = module.params - if module.check_mode: - return - if not params['dn'] and not params['filter']: - module.fail_json(msg='need dn or filter to delte an object', **result) - if params['dn']: - obj = udm_mod.get( params['dn'] ) - if not module.check_mode: - obj.delete() - stats.changed_objects.append(obj.dn) - if params['filter']: - for baseobject in udm_mod.search(params['filter']): - obj = udm_mod.get( baseobject.dn ) - if not module.check_mode: - obj.delete() - stats.changed_objects.append(obj.dn) - - -def run_module(): - module_args = dict( - module = dict( - type = 'str', - required = True - ), - position = dict( - type = 'str', - required = False - ), - set_properties = dict( - type = 'list', - required = False - ), - unset_properties = dict( - type = 'list', - required = False - ), - dn = dict( - type='str', - required=False - ), - filter = dict( - type='str', - required=False - ), - state = dict( - type='str', - default='present', - coices=['present','absent'], - required=False - ), - options = dict( - type = 'list', - required = False - ), - policies = dict( - type = 'list', - required = False - ), - ) - - module = AnsibleModule( - argument_spec=module_args, - supports_check_mode=True - ) - - result = dict( - changed=False, - meta=dict(changed_objects=[]), - message='' - ) - - if not have_udm: - module.fail_json(msg='The Python "univention.udm" is not available', **result) - - params = module.params - udm_con = UDM.admin() # connection to UDM - udm_con.version(1) - udm_mod = udm_con.get(module.params['module']) - stats = Stats() - - if params['state'] == 'present': - if params['dn']: - _create_or_modify_object(udm_mod,module,stats) - if params['filter']: - for obj in udm_mod.search(params['filter']): - _modify_object(udm_mod,module,obj,stats) - if not params['dn'] and not params['filter']: - _create_object(udm_mod,module,stats) - elif params['state'] == 'absent': - _remove_objects(udm_mod,module,stats) - result['meta']['changed_objects'] = stats.changed_objects - result['meta']['message'] = 'changed objects: %s' " ".join(stats.changed_objects) - - module.exit_json(**result) - -if __name__ == '__main__': - run_module()