diff --git a/plugins/module_utils/api.py b/plugins/module_utils/api.py index f78c5cee..81fdf589 100644 --- a/plugins/module_utils/api.py +++ b/plugins/module_utils/api.py @@ -85,6 +85,7 @@ NIOS_DTC_TOPOLOGY = 'dtc:topology' NIOS_EXTENSIBLE_ATTRIBUTE = 'extensibleattributedef' NIOS_VLAN = 'vlan' +NIOS_ADMINUSER = 'adminuser' NIOS_PROVIDER_SPEC = { 'host': dict(fallback=(env_fallback, ['INFOBLOX_HOST'])), @@ -696,7 +697,7 @@ def compare_objects(self, current_object, proposed_object): # If the lists are of a different length, the objects cannot be # equal, and False will be returned before comparing the list items # this code part will work for members' assignment - if (key in ('members', 'options', 'delegate_to', 'forwarding_servers', 'stub_members') + if (key in ('members', 'options', 'delegate_to', 'forwarding_servers', 'stub_members', 'ssh_keys') and (len(proposed_item) != len(current_item))): return False @@ -704,19 +705,20 @@ def compare_objects(self, current_object, proposed_object): if key in ('external_servers', 'list_values') and not self.verify_list_order(proposed_item, current_item): return False - for subitem in proposed_item: - if current_item: - # Host IPv4addrs wont contain use_nextserver and nextserver - # If DHCP is false. - dhcp_flag = current_item[0].get('configure_for_dhcp', False) - use_nextserver = subitem.get('use_nextserver', False) - if key == 'ipv4addrs' and not dhcp_flag: - subitem.pop('use_nextserver', None) - subitem.pop('nextserver', None) - elif key == 'ipv4addrs' and dhcp_flag and not use_nextserver: - subitem.pop('nextserver', None) - if not self.issubset(subitem, current_item): - return False + if key == 'ipv4addrs': + for subitem in proposed_item: + if current_item: + # Host IPv4addrs wont contain use_nextserver and nextserver + # If DHCP is false. + dhcp_flag = current_item[0].get('configure_for_dhcp', False) + use_nextserver = subitem.get('use_nextserver', False) + if key == 'ipv4addrs' and not dhcp_flag: + subitem.pop('use_nextserver', None) + subitem.pop('nextserver', None) + elif key == 'ipv4addrs' and dhcp_flag and not use_nextserver: + subitem.pop('nextserver', None) + if not self.issubset(subitem, current_item): + return False # If the lists are of a different length, the objects and order of element mismatch # Ignore DHCP options while comparing due to extra num param is get response @@ -766,6 +768,11 @@ def get_object_ref(self, module, ib_obj_type, obj_filter, ib_spec): except TypeError: name = obj_filter['name'] + return_fields = list(ib_spec.keys()) + + if (ib_obj_type == NIOS_ADMINUSER): + if 'password' in return_fields: return_fields.remove('password') + if old_name and new_name: if (ib_obj_type == NIOS_HOST_RECORD): # to check only by old_name if dns bypassing is set @@ -789,7 +796,7 @@ def get_object_ref(self, module, ib_obj_type, obj_filter, ib_spec): else: test_obj_filter = dict([('name', old_name)]) # get the object reference - ib_obj = self.get_object(ib_obj_type, test_obj_filter, return_fields=list(ib_spec.keys())) + ib_obj = self.get_object(ib_obj_type, test_obj_filter, return_fields=return_fields) if ib_obj: obj_filter['name'] = new_name elif old_ipv4addr_exists and (len(ib_obj) == 0): @@ -860,7 +867,7 @@ def get_object_ref(self, module, ib_obj_type, obj_filter, ib_spec): # check if test_obj_filter is empty copy passed obj_filter else: test_obj_filter = obj_filter - return_fields = list(ib_spec.keys()) + if ib_obj_type == NIOS_HOST_RECORD: ipv4addrs_return = [ 'ipv4addrs.ipv4addr', 'ipv4addrs.mac', 'ipv4addrs.configure_for_dhcp', 'ipv4addrs.host', @@ -871,6 +878,7 @@ def get_object_ref(self, module, ib_obj_type, obj_filter, ib_spec): ] return_fields.extend(ipv4addrs_return) return_fields.extend(ipv6addrs_return) + ib_obj = self.get_object(ib_obj_type, test_obj_filter.copy(), return_fields=return_fields) # prevents creation of a new A record with 'new_ipv4addr' when A record with a particular 'old_ipv4addr' is not found diff --git a/plugins/modules/nios_adminuser.py b/plugins/modules/nios_adminuser.py new file mode 100644 index 00000000..7e49cfcd --- /dev/null +++ b/plugins/modules/nios_adminuser.py @@ -0,0 +1,292 @@ +#!/usr/bin/python +# Copyright (c) 2018-2019 Red Hat, Inc. +# Copyright (c) 2020 Infoblox, Inc. +# 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: nios_adminuser +author: "Nitish K S (@nitish-ks)" +short_description: Configure Infoblox NIOS Adminuser +version_added: "1.8.0" +description: + - Adds and/or removes instances of adminuser objects from + Infoblox NIOS servers. This module manages NIOS C(adminuser) objects + using the Infoblox WAPI interface over REST. +requirements: + - infoblox-client +extends_documentation_fragment: infoblox.nios_modules.nios +notes: + - This module supports C(check_mode). +options: + name: + description: + - Specifies the adminuser name to add or remove from the system. + Users can also update the name as it is possible + to pass a dict containing I(new_name), I(old_name). See examples. + required: true + type: str + admin_groups: + description: + - The names of the Admin Groups to which this Admin User belongs. + Currently, this is limited to only one Admin Group. + required: true + type: list + password: + description: + - The password for the administrator to use when logging in. + type: str + no_log: true + auth_method: + description: + - Authentication method for the admin user. + default: KEYPAIR + choices: + - KEYPAIR + - KEYPAIR_PASSWORD + type: str + auth_type: + description: + - Authentication type for the admin user. + default: LOCAL + choices: + - LOCAL + - REMOTE + - SAML + - SAML_LOCAL + type: str + ca_certificate_issuer: + description: + - The CA certificate that is used for user lookup during authentication. + type: str + client_certificate_serial_number: + description: + - The serial number of the client certificate. + type: str + disable: + description: + - Determines whether the admin user is disabled or not. When this is set + to False, the admin user is enabled. + default: false + type: bool + email: + description: + - Email address of the admin user. + type: str + enable_certificate_authentication: + description: + - Determines whether the user is allowed to log in only with the + certificate. Regular username/password authentication will be disabled + for this user. + default: false + type: bool + time_zone: + description: + - The time zone for this admin user. + default: UTC + type: str + use_time_zone: + description: + - Use flag for I(time_zone). + default: false + type: bool + ssh_keys: + description: + - List of SSH keys for the admin user. + type: list + default: [] + elements: dict + suboptions: + key_name: + description: + - Unique identifier for the key + type: str + key_type: + description: + - Type of the SSH key. + choices: + - ECDSA + - ED25519 + - RSA + type: str + key_value: + description: + - SSH key text. + type: str + use_ssh_keys: + description: + - Enable/disable the ssh keypair authentication. + default: false + type: bool + extattrs: + description: + - Allows for the configuration of Extensible Attributes on the + instance of the object. This argument accepts a set of key / value + pairs for configuration. + type: dict + comment: + description: + - Configures a text string comment to be associated with the instance + of this object. The provided text string will be configured on the + object instance. + type: str + state: + description: + - Configures the intended state of the instance of the object on + the NIOS server. When this value is set to C(present), the object + is configured on the device and when this value is set to C(absent) + the value is removed (if necessary) from the device. + default: present + choices: + - present + - absent + type: str +''' + +EXAMPLES = ''' +- name: Create a new admin user + infoblox.nios_modules.nios_adminuser: + name: ansible_user + admin_groups: admin-group + password: "secure_password" + state: present + provider: + host: "{{ inventory_hostname_short }}" + username: admin + password: admin + connection: local + +- name: Update admin user name + infoblox.nios_modules.nios_adminuser: + name: {new_name: new_user, old_name: ansible_user} + admin_groups: admin-group + state: present + provider: + host: "{{ inventory_hostname_short }}" + username: admin + password: admin + connection: local + +- name: Create admin user with remote authentication + infoblox.nios_modules.nios_adminuser: + name: remote_admin_user + admin_groups: admin-group + auth_type: "REMOTE" + email: "admin@example.com" + use_time_zone: true + time_zone: 'US/Hawaii' + extattrs: + Site: "USA" + state: present + provider: + host: "{{ inventory_hostname_short }}" + username: admin + password: admin + connection: local + +- name: Create admin user with ssh keys + infoblox.nios_modules.nios_adminuser: + name: cloud_user + admin_groups: cloud-api-only + comment: "Created by Ansible" + disable : false + password: "secure_password" + use_ssh_keys: true + ssh_keys: + - key_name: "sshkey1" + key_type: "RSA" + key_value: "ssh-rsa AAAAB..." + - key_name: "sshkey2" + key_type: "ECDSA" + key_value: "ecdsa-sha2-nistp256 AAAAE..." + state: present + provider: + host: "{{ inventory_hostname_short }}" + username: admin + password: admin + connection: local + +- name: Update admin user to enable certificate authentication + infoblox.nios_modules.nios_adminuser: + name: admin_user + admin_groups: admin-group + enable_certificate_authentication: true + ca_certificate_issuer: "cacertificate/b25lLmVhcF9j..." + client_certificate_serial_number: "397F9435000100000031" + state: present + provider: + host: "{{ inventory_hostname_short }}" + username: admin + password: admin + connection: local + +- name: Remove admin user + infoblox.nios_modules.nios_adminuser: + name: new_user + admin_groups: admin-group + state: absent + provider: + host: "{{ inventory_hostname_short }}" + username: admin + password: admin + connection: local +''' + +RETURN = ''' # ''' + +from ansible.module_utils.basic import AnsibleModule +from ..module_utils.api import WapiModule +from ..module_utils.api import NIOS_ADMINUSER +from ..module_utils.api import normalize_ib_spec + + +def main(): + ''' Main entry point for module execution + ''' + ssh_key_spec = dict( + key_name=dict(type='str'), + key_type=dict(type='str', choices=['ECDSA', 'ED25519', 'RSA']), + key_value=dict(type='str') + ) + + ib_spec = dict( + name=dict(required=True, ib_req=True), + admin_groups=dict(type='list', required=True, ib_req=True), + password=dict(no_log=True), + auth_method=dict(default='KEYPAIR', choices=['KEYPAIR', 'KEYPAIR_PASSWORD']), + auth_type=dict(default='LOCAL', choices=['LOCAL', 'REMOTE', 'SAML', 'SAML_LOCAL']), + ca_certificate_issuer=dict(), + client_certificate_serial_number=dict(), + disable=dict(type='bool',default=False), + email=dict(), + enable_certificate_authentication=dict(type='bool',default=False), + time_zone=dict(default='UTC'), + use_time_zone=dict(type='bool',default=False), + ssh_keys=dict(type='list', default=[], elements='dict', options=ssh_key_spec), + use_ssh_keys=dict(type='bool',default=False), + extattrs=dict(type='dict'), + comment=dict() + ) + + argument_spec = dict( + provider=dict(required=True), + state=dict(default='present', choices=['present', 'absent']) + ) + + argument_spec.update(normalize_ib_spec(ib_spec)) + argument_spec.update(WapiModule.provider_spec) + + module = AnsibleModule(argument_spec=argument_spec, + supports_check_mode=True) + + wapi = WapiModule(module) + result = wapi.run(NIOS_ADMINUSER, ib_spec) + + module.exit_json(**result) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/tests/integration/targets/nios_adminuser/aliases b/tests/integration/targets/nios_adminuser/aliases new file mode 100644 index 00000000..b3138dc7 --- /dev/null +++ b/tests/integration/targets/nios_adminuser/aliases @@ -0,0 +1,3 @@ +shippable/cloud/group1 +cloud/nios +destructive diff --git a/tests/integration/targets/nios_adminuser/defaults/main.yaml b/tests/integration/targets/nios_adminuser/defaults/main.yaml new file mode 100644 index 00000000..9ef5ba51 --- /dev/null +++ b/tests/integration/targets/nios_adminuser/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: "*" +test_items: [] diff --git a/tests/integration/targets/nios_adminuser/meta/main.yaml b/tests/integration/targets/nios_adminuser/meta/main.yaml new file mode 100644 index 00000000..a4ad32ff --- /dev/null +++ b/tests/integration/targets/nios_adminuser/meta/main.yaml @@ -0,0 +1,3 @@ +--- +dependencies: + - prepare_nios_tests diff --git a/tests/integration/targets/nios_adminuser/tasks/main.yaml b/tests/integration/targets/nios_adminuser/tasks/main.yaml new file mode 100644 index 00000000..2c6918d2 --- /dev/null +++ b/tests/integration/targets/nios_adminuser/tasks/main.yaml @@ -0,0 +1,3 @@ +--- +- name: Include idempotence tasks for adminuser + ansible.builtin.include_tasks: nios_adminuser_idempotence.yml diff --git a/tests/integration/targets/nios_adminuser/tasks/nios_adminuser_idempotence.yml b/tests/integration/targets/nios_adminuser/tasks/nios_adminuser_idempotence.yml new file mode 100644 index 00000000..b40dbce1 --- /dev/null +++ b/tests/integration/targets/nios_adminuser/tasks/nios_adminuser_idempotence.yml @@ -0,0 +1,151 @@ +--- +- name: Cleanup adminuser + nios_adminuser: + name: ansible-adminuser + admin_groups: admin-group + state: absent + provider: "{{ nios_provider }}" + +- name: Configure a adminuser on the system + nios_adminuser: + name: ansible-adminuser + admin_groups: admin-group + password: Pwd@1234 + state: present + provider: "{{ nios_provider }}" + register: adminuser_create1 + +- name: Configure another adminuser on the system + nios_adminuser: + name: ansible-adminuser + admin_groups: admin-group + state: present + provider: "{{ nios_provider }}" + register: adminuser_create2 + +- name: Update the comment and ext attributes for an existing adminuser + nios_adminuser: + name: ansible-adminuser + admin_groups: admin-group + comment: this is an example comment + extattrs: + Site: west-dc + state: present + provider: "{{ nios_provider }}" + register: adminuser_update1 + +- name: Update again the comment and ext attributes for an existing adminuser + nios_adminuser: + name: ansible-adminuser + admin_groups: admin-group + comment: this is an example comment + extattrs: + Site: west-dc + state: present + provider: "{{ nios_provider }}" + register: adminuser_update2 + +- name: Remove the adminuser + nios_adminuser: + name: ansible-adminuser + admin_groups: admin-group + state: absent + provider: "{{ nios_provider }}" + register: adminuser_delete1 + +- name: Remove again the adminuser + nios_adminuser: + name: ansible-adminuser + admin_groups: admin-group + state: absent + provider: "{{ nios_provider }}" + register: adminuser_delete2 + +- name: Create a new adminuser with all attributes + infoblox.nios_modules.nios_adminuser: + name: ansible-adminuser + admin_groups: admin-group + comment: "Created by Ansible" + auth_method: KEYPAIR_PASSWORD + auth_type: LOCAL + use_ssh_keys: true + ssh_keys: + - key_name: "ansiblekey" + key_type: "RSA" + key_value: "ssh-rsa AAAA..." + password: "Pwd@1234" + ca_certificate_issuer: "cacertificate/b25lLmVhcF9j..." + client_certificate_serial_number: "397F9435000100000031" + disable : false + email: "example@email.com" + enable_certificate_authentication: true + time_zone: "UTC" + use_time_zone: true + extattrs: + Site: "USA" + state: present + provider: "{{ nios_provider }}" + register: adminuser_create3 + +- name: Rerun adminuser with all attributes + infoblox.nios_modules.nios_adminuser: + name: ansible-adminuser + admin_groups: admin-group + comment: "Created by Ansible" + auth_method: KEYPAIR_PASSWORD + auth_type: LOCAL + use_ssh_keys: true + ssh_keys: + - key_name: "ansiblekey" + key_type: "RSA" + key_value: "ssh-rsa AAAA..." + ca_certificate_issuer: "cacertificate/b25lLmVhcF9j..." + client_certificate_serial_number: "397F9435000100000031" + disable : false + email: "example@email.com" + enable_certificate_authentication: true + time_zone: "UTC" + use_time_zone: true + extattrs: + Site: "USA" + state: present + provider: "{{ nios_provider }}" + register: adminuser_create3 + +- name: Delete adminuser + infoblox.nios_modules.nios_adminuser: + name: ansible-adminuser + admin_groups: admin-group + comment: "Created by Ansible" + auth_method: KEYPAIR_PASSWORD + auth_type: LOCAL + use_ssh_keys: true + ssh_keys: + - key_name: "ansiblekey" + key_type: "RSA" + key_value: "ssh-rsa AAAA..." + ca_certificate_issuer: "cacertificate/b25lLmVhcF9j..." + client_certificate_serial_number: "397F9435000100000031" + disable : false + email: "example@email.com" + enable_certificate_authentication: true + time_zone: "UTC" + use_time_zone: true + extattrs: + Site: "USA" + state: absent + provider: "{{ nios_provider }}" + register: adminuser_delete3 + +- name: Assert changes in adminuser + ansible.builtin.assert: + that: + - adminuser_create1.changed + - not adminuser_create2.changed + - adminuser_update1.changed + - not adminuser_update2.changed + - adminuser_delete1.changed + - not adminuser_delete2.changed + - adminuser_create3.changed + - not adminuser_create3.changed + - adminuser_delete3.changed diff --git a/tests/integration/targets/nios_vlan/aliases b/tests/integration/targets/nios_vlan/aliases new file mode 100644 index 00000000..b3138dc7 --- /dev/null +++ b/tests/integration/targets/nios_vlan/aliases @@ -0,0 +1,3 @@ +shippable/cloud/group1 +cloud/nios +destructive diff --git a/tests/integration/targets/nios_vlan/defaults/main.yaml b/tests/integration/targets/nios_vlan/defaults/main.yaml new file mode 100644 index 00000000..9ef5ba51 --- /dev/null +++ b/tests/integration/targets/nios_vlan/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: "*" +test_items: [] diff --git a/tests/integration/targets/nios_vlan/meta/main.yaml b/tests/integration/targets/nios_vlan/meta/main.yaml new file mode 100644 index 00000000..a4ad32ff --- /dev/null +++ b/tests/integration/targets/nios_vlan/meta/main.yaml @@ -0,0 +1,3 @@ +--- +dependencies: + - prepare_nios_tests diff --git a/tests/integration/targets/nios_vlan/tasks/main.yaml b/tests/integration/targets/nios_vlan/tasks/main.yaml new file mode 100644 index 00000000..1862fa6e --- /dev/null +++ b/tests/integration/targets/nios_vlan/tasks/main.yaml @@ -0,0 +1,3 @@ +--- +- name: Include idempotence tasks for vlan + ansible.builtin.include_tasks: nios_vlan_idempotence.yml diff --git a/tests/integration/targets/nios_vlan/tasks/nios_vlan_idempotence.yml b/tests/integration/targets/nios_vlan/tasks/nios_vlan_idempotence.yml new file mode 100644 index 00000000..0c8cdd49 --- /dev/null +++ b/tests/integration/targets/nios_vlan/tasks/nios_vlan_idempotence.yml @@ -0,0 +1,90 @@ +--- +- name: Cleanup VLAN + nios_vlan: + name: ansible-vlan + id: 10 + parent: default + state: absent + provider: "{{ nios_provider }}" + +- name: Configure a VLAN on the system + nios_vlan: + name: ansible-vlan + id: 10 + parent: default + contact: 'example@email.com' + department: 'IT' + description: 'This is an example VLAN' + reserved: true + state: absent + provider: "{{ nios_provider }}" + register: vlan_create1 + +- name: Configure another VLAN on the system + name: ansible-vlan + id: 10 + parent: default + contact: 'example@email.com' + department: 'IT' + description: 'This is an example VLAN' + reserved: true + state: absent + provider: "{{ nios_provider }}" + register: vlan_create2 + +- name: Update the comment and ext attributes for an existing VLAN + name: ansible-vlan + id: 10 + parent: default + contact: 'example@email.com' + department: 'IT' + description: 'This is an example VLAN' + reserved: true + comment: this is an example comment + extattrs: + Site: west-dc + state: absent + provider: "{{ nios_provider }}" + register: vlan_update1 + +- name: Update again the comment and ext attributes for an existing VLAN + name: ansible-vlan + id: 10 + parent: default + contact: 'example@email.com' + department: 'IT' + description: 'This is an example VLAN' + reserved: true + comment: this is an example comment + extattrs: + Site: west-dc + state: absent + provider: "{{ nios_provider }}" + register: vlan_update2 + +- name: Remove the VLAN + nios_vlan: + name: ansible-vlan + id: 10 + parent: default + state: absent + provider: "{{ nios_provider }}" + register: vlan_delete1 + +- name: Remove again the VLAN + name: ansible-vlan + id: 10 + parent: default + state: absent + provider: "{{ nios_provider }}" + register: vlan_delete2 + +- name: Assert changes in VLAN + ansible.builtin.assert: + that: + - vlan_create1.changed + - not vlan_create2.changed + - vlan_update1.changed + - not vlan_update2.changed + - vlan_delete1.changed + - not vlan_delete2.changed