#!/usr/bin/python

# (c) 2023, NetApp, 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: na_ontap_vserver_audit
short_description: NetApp Ontap - create, delete or modify vserver audit configuration.
extends_documentation_fragment:
    - netapp.ontap.netapp.na_ontap
version_added: '22.3.0'
author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com>
description:
  - Create, delete or modify vserver audit configuration.
options:
  state:
    description:
      - Whether the specified vserver audit configuration should exist or not.
    choices: ['present', 'absent']
    default: present
    type: str
  vserver:
    description:
      - Specifies name of the Vserver.
    required: true
    type: str
  log_path:
    description:
      - The audit log destination path where consolidated audit logs are stored.
    type: str
  guarantee:
    description:
      - Indicates whether there is a strict Guarantee of Auditing.
      - This option requires ONTAP 9.10.1 or later.
    type: bool
  enabled:
    description:
      - Specifies whether or not auditing is enabled on the SVM.
    type: bool
  events:
    description:
      - Specifies events for which auditing is enabled on the SVM.
    type: dict
    suboptions:
      authorization_policy:
        description:
          - Authorization policy change events.
        type: bool
      cap_staging:
        description:
          - Central access policy staging events.
        type: bool
      cifs_logon_logoff:
        description:
          - CIFS logon and logoff events.
        type: bool
      file_operations:
        description:
          - File operation events.
        type: bool
      file_share:
        description:
          - File share category events.
        type: bool
      security_group:
        description:
          - Local security group management events.
        type: bool
      user_account:
        description:
          - Local user account management events.
        type: bool
  log:
    description:
      - Specifies events for which auditing is enabled on the SVM.
    type: dict
    suboptions:
      format:
        description:
          - This option describes the format in which the logs are generated by consolidation process.
            Possible values are,
          - xml - Data ONTAP-specific XML log format
          - evtx - Microsoft Windows EVTX log format
        choices: ['xml', 'evtx']
        type: str
      retention:
        description:
          - This option describes the count and time to retain the audit log file.
        type: dict
        suboptions:
          count:
            description:
              - Determines how many audit log files to retain before rotating the oldest log file out.
              - This is mutually exclusive with duration.
            type: int
          duration:
            description:
              - Specifies an ISO-8601 format date and time to retain the audit log file.
              - The audit log files are deleted once they reach the specified date/time.
              - This is mutually exclusive with count.
            type: str
      rotation:
        description:
          - Audit event log files are rotated when they reach a configured threshold log size or are on a configured schedule.
          - When an event log file is rotated, the scheduled consolidation task first renames the active converted file to a time-stamped archive file,
            and then creates a new active converted event log file.
        type: dict
        suboptions:
          size:
            description:
              - Rotates logs based on log size in bytes.
              - Default value is 104857600.
            type: int

notes:
  - This module supports REST only.
  - At least one event should be enabled.
  - No other fields can be specified when enabled is specified for modify.
"""

EXAMPLES = """

    - name: Create vserver audit configuration
      netapp.ontap.na_ontap_vserver_audit:
        state: present
        vserver: ansible
        enabled: True
        events:
          authorization_policy: False
          cap_staging: False
          cifs_logon_logoff: True
          file_operations: True
          file_share: False
          security_group: False
          user_account: False
        log_path: "/"
        log:
          format: xml
          retention:
            count: 4
          rotation:
            size: "1048576"
        guarantee: False
        hostname: "{{ netapp_hostname }}"
        username: "{{ netapp_username }}"
        password: "{{ netapp_password }}"

    - name: Modify vserver audit configuration
      netapp.ontap.na_ontap_vserver_audit:
        state: present
        vserver: ansible
        enabled: True
        events:
          authorization_policy: True
          cap_staging: True
          cifs_logon_logoff: True
          file_operations: True
          file_share: True
          security_group: True
          user_account: True
        log_path: "/tmp"
        log:
          format: evtx
          retention:
            count: 5
          rotation:
            size: "104857600"
        guarantee: True
        hostname: "{{ netapp_hostname }}"
        username: "{{ netapp_username }}"
        password: "{{ netapp_password }}"

    - name: Delete vserver audit configuration
      netapp.ontap.na_ontap_vserver_audit:
        state: absent
        vserver: ansible
        hostname: "{{ netapp_hostname }}"
        username: "{{ netapp_username }}"
        password: "{{ netapp_password }}"
"""

RETURN = """
"""

import time
from ansible.module_utils.basic import AnsibleModule
import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils
from ansible_collections.netapp.ontap.plugins.module_utils.netapp_module import NetAppModule
from ansible_collections.netapp.ontap.plugins.module_utils import rest_generic


class NetAppONTAPVserverAudit:
    """
    Class with vserver audit configuration methods
    """

    def __init__(self):

        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(dict(
            state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
            vserver=dict(required=True, type='str'),
            enabled=dict(required=False, type='bool'),
            guarantee=dict(required=False, type='bool'),
            log_path=dict(required=False, type='str'),
            log=dict(type='dict', options=dict(
                format=dict(type='str', choices=['xml', 'evtx']),
                retention=dict(type='dict', options=dict(
                    count=dict(type='int'),
                    duration=dict(type='str'),
                )),
                rotation=dict(type='dict', options=dict(
                    size=dict(type='int'),
                )),
            )),
            events=dict(type='dict', options=dict(
                authorization_policy=dict(type='bool'),
                cap_staging=dict(type='bool'),
                cifs_logon_logoff=dict(type='bool'),
                file_operations=dict(type='bool'),
                file_share=dict(type='bool'),
                security_group=dict(type='bool'),
                user_account=dict(type='bool'),
            ))
        ))

        self.module = AnsibleModule(
            argument_spec=self.argument_spec,
            supports_check_mode=True
        )

        # set up variables
        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.filter_out_none_entries(self.na_helper.set_parameters(self.module.params))

        self.rest_api = netapp_utils.OntapRestAPI(self.module)
        self.rest_api.fail_if_not_rest_minimum_version('na_ontap_vserver_audit', 9, 6)
        partially_supported_rest_properties = [['guarantee', (9, 10, 1)]]
        self.use_rest = self.rest_api.is_rest_supported_properties(self.parameters, None, partially_supported_rest_properties)
        self.svm_uuid = None
        if 'events' in self.parameters and self.parameters['state'] == 'present':
            if all(self.parameters['events'][value] is False for value in self.parameters['events']) is True:
                self.module.fail_json(msg="Error: At least one event should be enabled")

    def get_vserver_audit_configuration_rest(self):
        """
        Retrieves audit configurations.
        """
        api = "protocols/audit"
        query = {
            'svm.name': self.parameters['vserver'],
            'fields': 'svm.uuid,enabled,events,log,log_path,'
        }
        if self.rest_api.meets_rest_minimum_version(self.use_rest, 9, 10, 1):
            query['fields'] += 'guarantee,'
        record, error = rest_generic.get_one_record(self.rest_api, api, query)
        if error:
            self.module.fail_json(msg="Error on fetching vserver audit configuration: %s" % error)
        if record:
            self.svm_uuid = self.na_helper.safe_get(record, ['svm', 'uuid'])
            return {
                'enabled': self.na_helper.safe_get(record, ['enabled']),
                'events': self.na_helper.safe_get(record, ['events']),
                'log': self.na_helper.safe_get(record, ['log']),
                'log_path': self.na_helper.safe_get(record, ['log_path']),
                'guarantee': record.get('guarantee', False),
            }
        return record

    def create_vserver_audit_config_body_rest(self):
        """
        Vserver audit config body for create and modify with rest API.
        """
        body = {}
        if 'events' in self.parameters:
            body['events'] = self.parameters['events']
        if 'guarantee' in self.parameters:
            body['guarantee'] = self.parameters['guarantee']
        if self.na_helper.safe_get(self.parameters, ['log', 'retention', 'count']):
            body['log.retention.count'] = self.parameters['log']['retention']['count']
        if self.na_helper.safe_get(self.parameters, ['log', 'retention', 'duration']):
            body['log.retention.duration'] = self.parameters['log']['retention']['duration']
        if self.na_helper.safe_get(self.parameters, ['log', 'rotation', 'size']):
            body['log.rotation.size'] = self.parameters['log']['rotation']['size']
        if self.na_helper.safe_get(self.parameters, ['log', 'format']):
            body['log.format'] = self.parameters['log']['format']
        if 'log_path' in self.parameters:
            body['log_path'] = self.parameters['log_path']
        return body

    def create_vserver_audit_configuration_rest(self):
        """
        Creates an audit configuration.
        """
        api = "protocols/audit"
        body = self.create_vserver_audit_config_body_rest()
        if 'vserver' in self.parameters:
            body['svm.name'] = self.parameters.get('vserver')
        if 'enabled' in self.parameters:
            body['enabled'] = self.parameters['enabled']
        record, error = rest_generic.post_async(self.rest_api, api, body)
        if error:
            self.module.fail_json(msg="Error on creating vserver audit configuration: %s" % error)

    def delete_vserver_audit_configuration_rest(self, current):
        """
        Deletes an audit configuration.
        """
        api = "protocols/audit/%s" % self.svm_uuid
        if current['enabled'] is True:
            modify = {'enabled': False}
            self.modify_vserver_audit_configuration_rest(modify)
            current = self.get_vserver_audit_configuration_rest()
        retry = 2
        while retry > 0:
            record, error = rest_generic.delete_async(self.rest_api, api, None)
            # Delete throws retry after sometime error during first run by default, hence retrying after sometime.
            if error and '9699350' in error:
                time.sleep(120)
                retry -= 1
            elif error:
                self.module.fail_json(msg="Error on deleting vserver audit configuration: %s" % error)
            else:
                return

    def modify_vserver_audit_configuration_rest(self, modify):
        """
        Updates audit configuration.
        """
        body = {}
        if 'enabled' in modify:
            body['enabled'] = modify['enabled']
        else:
            body = self.create_vserver_audit_config_body_rest()
        api = "protocols/audit"
        record, error = rest_generic.patch_async(self.rest_api, api, self.svm_uuid, body)
        if error:
            self.module.fail_json(msg="Error on modifying vserver audit configuration: %s" % error)

    def apply(self):
        current = self.get_vserver_audit_configuration_rest()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        modify = self.na_helper.get_modified_attributes(current, self.parameters) if cd_action is None else None
        if self.na_helper.changed and not self.module.check_mode:
            if cd_action == 'create':
                self.create_vserver_audit_configuration_rest()
            elif cd_action == 'delete':
                self.delete_vserver_audit_configuration_rest(current)
            elif modify:
                # No other fields can be specified when enabled is specified for modify
                if 'enabled' in modify:
                    self.modify_vserver_audit_configuration_rest(modify)
                    modify.pop('enabled')
                if modify:
                    # This method will be called to modify fields other than enabled
                    self.modify_vserver_audit_configuration_rest(modify)
        result = netapp_utils.generate_result(self.na_helper.changed, cd_action)
        self.module.exit_json(**result)


def main():
    """
    Creates the NetApp Ontap vserver audit configuration object and runs the correct play task
    """
    obj = NetAppONTAPVserverAudit()
    obj.apply()


if __name__ == '__main__':
    main()
