From 3ca9982c73646694a5fc1865487706f5ae0cb9af Mon Sep 17 00:00:00 2001 From: chrisp <chris@chrisprocter.co.uk> Date: Tue, 12 Jan 2021 17:20:32 +0000 Subject: [PATCH] New automount key management module There is a new automount key module placed in the plugins folder: plugins/modules/ipaautomountkey.py The server module allows to ensure presence and absence of automount keys. The module requires an existing automount location and map to place the key within. Here is the documentation for the module: README-automountkey.md New example playbooks have been added: playbooks/automount/automount-key-absent.yaml playbooks/automount/automount-key-present.yaml New tests for the module: tests/automount/test_automountkey.yml --- README-automountkey.md | 104 +++++++++ .../automount/.automount-map-present.yml.swp | Bin 0 -> 12288 bytes plugins/modules/ipaautomountkey.py | 217 ++++++++++++++++++ tests/automount/test_automountkey.yml | 180 +++++++++++++++ 4 files changed, 501 insertions(+) create mode 100644 README-automountkey.md create mode 100644 playbooks/automount/.automount-map-present.yml.swp create mode 100644 plugins/modules/ipaautomountkey.py create mode 100644 tests/automount/test_automountkey.yml diff --git a/README-automountkey.md b/README-automountkey.md new file mode 100644 index 00000000..a4c1ab69 --- /dev/null +++ b/README-automountkey.md @@ -0,0 +1,104 @@ +Automountkey module +===================== + +Description +----------- + +The automountkey module allows the addition and removal of keys within an automount map. + +It is desgined to follow the IPA api as closely as possible while ensuring ease of use. + + +Features +-------- +* Automount key management + +Supported FreeIPA Versions +-------------------------- + +FreeIPA versions 4.4.0 and up are supported by the ipaautomountkey module. + +Requirements +------------ +**Controller** +* Ansible version: 2.8+ + +**Node** +* Supported FreeIPA version (see above) + + +Usage +===== + +Example inventory file + +```ini +[ipaserver] +ipaserver.test.local +``` + + +Example playbook to ensure presence of an automount map: + +```yaml +--- +- name: Playbook to add an automount map + hosts: ipaserver + become: true + + tasks: + - name: create key TestKey + ipaautomountkey: + ipaadmin_password: SomeADMINpassword + location: TestLocation + mapname: TestMap + key: TestKey + info: 192.168.122.1:/exports + state: present + + - name: ensure key TestKey is absent + ipaautomountkey: + ipaadmin_password: SomeADMINpassword + location: TestLocation + mapname: TestMap + key: TestKey + state: absent +``` + +Example playbook to rename an automount map: + +```yaml +--- +- name: Playbook to add an automount map + - name: ensure key TestKey has been renamed to NewKeyName + ipaautomountkey: + ipaadmin_password: password01 + automountlocationcn: TestLocation + automountmapname: TestMap + automountkey: TestKey + newname: NewKeyName + state: rename +``` + + +Variables +========= + +ipaautomountkey +------- + +Variable | Description | Required +-------- | ----------- | -------- +`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no +`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no +`location` \| `automountlocationcn` \| `automountlocation` | Location name. | yes +`mapname` \| `map` \| `automountmapname` \| `automountmap` | Map the key belongs to | yes +`key` \| `name` \| `automountkey` | Automount key to manage | yes +`newkey` \| newname` \| `newautomountkey` | the name to change the key to if state is `rename` | yes when state is `rename` +`info` \| `information` \| `automountinformation` | Mount information for the key | yes when state is `present` +`state` | The state to ensure. It can be one of `present`, or `absent`, default: `present`. | no + +Authors +======= + +Chris Procter diff --git a/playbooks/automount/.automount-map-present.yml.swp b/playbooks/automount/.automount-map-present.yml.swp new file mode 100644 index 0000000000000000000000000000000000000000..1a9bbc91a11e1d02cb72378a07472de6debf2cc1 GIT binary patch literal 12288 zcmYc?2=nw+u+TGNU|?Vn01*&;jD?vYIio1ExB$Y(N0uiR7Z~Y5R2StZm!uZyCFkpv zW~1w=gDBB2D9X=DO)e?cPs}UMOv*{sO)E-G%`8aNFUU!(Ov=yCF4j*hEy>T#FU>1K zuyk`13v>&LQj1gbO7tpobI?s6<&TEIXb9jB0p3t!BSQm_8f7I#1!1935OWldhQMeD zjE2By2#kinXb6mkz-S1JhQMeDjF1p0DPUx%XJBApg8G*aN;9I-Q0^!-8UmvsFd71* zAut*OqaiRF0;3@?8UmvsFd71*Aut*OqaiQ^Lm)AQfuWs`f#Ei&<1fI#0HQ$N_`}b@ zaFL&ZVIMyO!+L%OhGqN=3@Q8!3|{;U4D$R848r^j46OVN44?QI81C{hFx-Ke0n$8* zM?+vV1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz11V1Qcip7#K)1ytpK>B-Kg*x}ckA z&AJMic_pbud5Jj$wZL42!zL^y=_<JRMnTQZOUzBRg0eFU5))H$GxOpL5{rw=^NUif z6oT_}QypD=J^f%pV5K01u$AjM`N@eTnfZAjLv$6u#wobw6_*yJD!^B_E5H;LgKSnv z%u9iLPa&}=6)AK|5{t8oK}IE|Cg<m-S}Bwim8LQ%C}iXpmlRtmWELbArxul^7BRrA Za)h}9ZU@XXh180~+=84`23=iU1^_ajy?p=x literal 0 HcmV?d00001 diff --git a/plugins/modules/ipaautomountkey.py b/plugins/modules/ipaautomountkey.py new file mode 100644 index 00000000..772ff2ed --- /dev/null +++ b/plugins/modules/ipaautomountkey.py @@ -0,0 +1,217 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Authors: +# Chris Procter <cprocter@redhat.com> +# +# Copyright (C) 2021 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program 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. +# +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + +ANSIBLE_METADATA = { + "metadata_version": "1.0", + "supported_by": "community", + "status": ["preview"], +} + + +DOCUMENTATION = ''' +--- +module: ipaautomountkey +author: chris procter +short_description: Manage FreeIPA autommount map +description: +- Add, delete, and modify an IPA automount map +options: + ipaadmin_principal: + description: The admin principal + default: admin + ipaadmin_password: + description: The admin password + required: False + location: + description: automount location map is in + required: True + choices: ["automountlocationcn", "automountlocation"] + mapname: + description: automount map to be managed + choices: ["map", "automountmapname", "automountmap"] + required: True + key: + description: automount key to be managed + required: True + choices: ["name", "automountkey"] + newkey: + description: key to change to if state=rename + required: True + choices: ["newname", "newautomountkey"] + info: + description: Mount information for the key + required: True + choices: ["information", "newinfo", "automountinformation"] + state: + description: State to ensure + required: False + default: present + choices: ["present", "absent", "rename"] +''' + +EXAMPLES = ''' + - name: create key TestKey + ipaautomountkey: + ipaadmin_password: SomeADMINpassword + locationcn: TestLocation + mapname: TestMap + key: TestKey + info: 192.168.122.1:/exports + state: present + + - name: ensure key TestKey is absent + ipaautomountkey: + ipaadmin_password: SomeADMINpassword + location: TestLocation + mapname: TestMap + key: TestKey + state: absent +''' + +RETURN = ''' +''' + +from ansible.module_utils.ansible_freeipa_module import ( + FreeIPABaseModule, ipalib_errors +) + + +class AutomountKey(FreeIPABaseModule): + + ipa_param_mapping = { + 'automountkey': "key", + 'automountmapautomountmapname': "mapname", + } + + def get_key(self, location, mapname, keyname): + resp = dict() + try: + resp = self.api_command("automountkey_show", + location, + {"automountmapautomountmapname": mapname, + "automountkey": keyname}) + except ipalib_errors.NotFound: + pass + + return resp.get("result", None) + + def check_ipa_params(self): + if not self.ipa_params.info and self.ipa_params.state == "present": + self.fail_json(msg="Value required for argument 'info'") + + if self.ipa_params.state == "rename" and \ + self.ipa_params.newname is None: + self.fail_json(msg="newname is required if state = 'rename'") + + def define_ipa_commands(self): + args = self.get_ipa_command_args() + key = self.get_key(self.ipa_params.location, + self.ipa_params.mapname, + self.ipa_params.key) + + if self.ipa_params.state == "present": + if key is None: + # does not exist and is wanted + args["automountinformation"] = self.ipa_params.info + self.add_ipa_command( + "automountkey_add", + name=self.ipa_params.location, + args=args + ) + elif key is not None: + # exists and is wanted, check for changes + if self.ipa_params.info != \ + key.get('automountinformation', [None])[0]: + args["newautomountinformation"] = self.ipa_params.info + self.add_ipa_command( + "automountkey_mod", + name=self.ipa_params.location, + args=args + ) + elif self.ipa_params.state == "rename": + if key is not None: + newkey = self.get_key(self.ipa_params.location, + self.ipa_params.mapname, + self.ipa_params.newname) + + args["rename"] = self.ipa_params.newname + if newkey is None: + self.add_ipa_command( + "automountkey_mod", + name=self.ipa_params.location, + args=args + ) + else: + # if key exists and self.ipa_params.state == "absent": + if key is not None: + self.add_ipa_command( + "automountkey_del", + name=self.ipa_params.location, + args=args + ) + + +def main(): + ipa_module = AutomountKey( + argument_spec=dict( + ipaadmin_principal=dict(type="str", + default="admin" + ), + ipaadmin_password=dict(type="str", + required=False, + no_log=True + ), + state=dict(type='str', + default='present', + choices=['present', 'absent', 'rename'] + ), + location=dict(type="str", + aliases=["automountlocationcn", "automountlocation"], + default=None, + required=True + ), + newname=dict(type="str", + aliases=["newkey", "new_name", + "new_key", "newautomountkey"], + default=None, + required=False + ), + mapname=dict(type="str", + aliases=["map", "automountmapname", "automountmap"], + default=None, + required=True + ), + key=dict(type="str", + required=True, + aliases=["name", "automountkey"] + ), + info=dict(type="str", + aliases=["information", "newinfo", + "automountinformation"] + ), + ), + ) + ipa_module.ipa_run() + + +if __name__ == "__main__": + main() diff --git a/tests/automount/test_automountkey.yml b/tests/automount/test_automountkey.yml new file mode 100644 index 00000000..19c8922a --- /dev/null +++ b/tests/automount/test_automountkey.yml @@ -0,0 +1,180 @@ +--- +- name: Test automountmap + hosts: ipaserver + become: true + gather_facts: false + + tasks: + - name: ensure location TestLocation is absent + ipaautomountlocation: + ipaadmin_password: SomeADMINpassword + name: TestLocation + state: absent + +# - name: ensure map TestMap is absent + # ipaautomountmap: + # ipaadmin_password: SomeADMINpassword + # name: TestMap + # location: TestLocation + # state: absent +# +# - name: ensure key TestKey is absent +# ipaautomountkey: +# ipaadmin_password: SomeADMINpassword +# automountlocationcn: TestLocation +# automountmapname: TestMap +# automountkey: TestKey +# state: absent + + - name: create location TestLocation + ipaautomountlocation: + ipaadmin_password: SomeADMINpassword + name: TestLocation + state: present + + - name: create map TestMap + ipaautomountmap: + ipaadmin_password: SomeADMINpassword + name: TestMap + location: TestLocation + desc: "this is a test map that should be deleted by the test" + +### test the key creation, and modification + - name: ensure key TestKey is present + ipaautomountkey: + ipaadmin_password: SomeADMINpassword + automountlocationcn: TestLocation + automountmapname: TestMap + automountkey: TestKey + automountinformation: 192.168.122.1:/exports + state: present + register: result + failed_when: result.failed or not result.changed + + - name: ensure key TestKey is present again + ipaautomountkey: + ipaadmin_password: SomeADMINpassword + automountlocationcn: TestLocation + automountmapname: TestMap + automountkey: TestKey + automountinformation: 192.168.122.1:/exports + state: present + register: result + failed_when: result.failed or result.changed + +## modify the key + - name: ensure key TestKey information has been updated + ipaautomountkey: + ipaadmin_password: SomeADMINpassword + automountlocationcn: TestLocation + automountmapname: TestMap + automountkey: TestKey + automountinformation: 192.168.122.1:/nfsshare + state: present + register: result + failed_when: result.failed or not result.changed + + - name: ensure key TestKey information has been updated again + ipaautomountkey: + ipaadmin_password: SomeADMINpassword + automountlocationcn: TestLocation + automountmapname: TestMap + automountkey: TestKey + automountinformation: 192.168.122.1:/nfsshare + state: present + register: result + failed_when: result.failed or result.changed + +## modify the name + - name: ensure key TestKey has been renamed to NewKeyName + ipaautomountkey: + ipaadmin_password: SomeADMINpassword + automountlocationcn: TestLocation + automountmapname: TestMap + automountkey: TestKey + automountinformation: 192.168.122.1:/nfsshare + newname: NewKeyName + state: rename + register: result + failed_when: result.failed or not result.changed + + - name: ensure key TestKey does not exist + ipaautomountkey: + ipaadmin_password: SomeADMINpassword + automountlocationcn: TestLocation + automountmapname: TestMap + automountkey: NewKeyName + automountinformation: 192.168.122.1:/nfsshare + state: present + register: result + failed_when: result.failed or result.changed + + - name: ensure key NewKeyName does exist + ipaautomountkey: + ipaadmin_password: SomeADMINpassword + automountlocationcn: TestLocation + automountmapname: TestMap + automountkey: NewKeyName + automountinformation: 192.168.122.1:/nfsshare + state: present + register: result + failed_when: result.failed or result.changed + + - name: ensure key TestKey has been renamed to NewKeyName again + ipaautomountkey: + ipaadmin_password: SomeADMINpassword + automountlocationcn: TestLocation + automountmapname: TestMap + automountkey: TestKey + automountinformation: 192.168.122.1:/nfsshare + newname: NewKeyName + state: rename + register: result + failed_when: result.failed or result.changed + + - name: ensure failure when state=present and newname is not set + ipaautomountkey: + ipaadmin_password: SomeADMINpassword + automountlocationcn: TestLocation + automountmapname: TestMap + automountkey: TestKey + automountinformation: 192.168.122.1:/nfsshare + state: rename + register: result + failed_when: not result.failed + + +### cleanup after the tests + - name: ensure key NewKeyName is absent + ipaautomountkey: + ipaadmin_password: SomeADMINpassword + automountlocationcn: TestLocation + automountmapname: TestMap + automountkey: NewKeyName + state: absent + register: result + failed_when: result.failed or not result.changed + + - name: ensure key TestKey is absent again + ipaautomountkey: + ipaadmin_password: SomeADMINpassword + automountlocationcn: TestLocation + automountmapname: TestMap + automountkey: NewKeyName + state: absent + register: result + failed_when: result.failed or result.changed + + - name: ensure map TestMap is removed + ipaautomountmap: + ipaadmin_password: SomeADMINpassword + name: TestMap + location: TestLocation + state: absent + + - name: ensure location TestLocation is removed + ipaautomountlocation: + ipaadmin_password: SomeADMINpassword + name: TestLocation + state: absent + -- GitLab