diff --git a/README-automountkey.md b/README-automountkey.md new file mode 100644 index 0000000000000000000000000000000000000000..a4c1ab6956b6fad684616aa55a6307313646d431 --- /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 Binary files /dev/null and b/playbooks/automount/.automount-map-present.yml.swp differ diff --git a/plugins/modules/ipaautomountkey.py b/plugins/modules/ipaautomountkey.py new file mode 100644 index 0000000000000000000000000000000000000000..772ff2ed64886d4a594ecb250d53f4c64b7666f3 --- /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 0000000000000000000000000000000000000000..19c8922a74c22a6ecdea958c61ceab3ec319091e --- /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 +