Exemplo n.º 1
0
class NetAppOntapServiceProcessorNetwork(object):
    """
        Modify a Service Processor Network
    """

    def __init__(self):
        """
            Initialize the NetAppOntapServiceProcessorNetwork class
        """
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(dict(
            state=dict(required=False, type='str', choices=['present'], default='present'),
            address_type=dict(required=True, type='str', choices=['ipv4', 'ipv6']),
            is_enabled=dict(required=True, type='bool'),
            node=dict(required=True, type='str'),
            dhcp=dict(required=False, type='str', choices=['v4', 'none']),
            gateway_ip_address=dict(required=False, type='str'),
            ip_address=dict(required=False, type='str'),
            netmask=dict(required=False, type='str'),
            prefix_length=dict(required=False, type='int'),
            wait_for_completion=dict(required=False, type='bool', default=False)
        ))

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

        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)
        self.set_playbook_zapi_key_map()

        if HAS_NETAPP_LIB is False:
            self.module.fail_json(
                msg="the python NetApp-Lib module is required")
        else:
            self.server = netapp_utils.setup_na_ontap_zapi(
                module=self.module, vserver=None)
        return

    def set_playbook_zapi_key_map(self):
        self.na_helper.zapi_string_keys = {
            'address_type': 'address-type',
            'node': 'node',
            'dhcp': 'dhcp',
            'gateway_ip_address': 'gateway-ip-address',
            'ip_address': 'ip-address',
            'netmask': 'netmask'
        }
        self.na_helper.zapi_int_keys = {
            'prefix_length': 'prefix-length'
        }
        self.na_helper.zapi_bool_keys = {
            'is_enabled': 'is-enabled',
        }
        self.na_helper.zapi_required = {
            'address_type': 'address-type',
            'node': 'node',
            'is_enabled': 'is-enabled'
        }

    def get_sp_network_status(self):
        """
        Return status of service processor network
        :param:
            name : name of the node
        :return: Status of the service processor network
        :rtype: dict
        """
        spn_get_iter = netapp_utils.zapi.NaElement('service-processor-network-get-iter')
        query_info = {
            'query': {
                'service-processor-network-info': {
                    'node': self.parameters['node'],
                    'address-type': self.parameters['address_type']
                }
            }
        }
        spn_get_iter.translate_struct(query_info)
        result = self.server.invoke_successfully(spn_get_iter, True)
        if int(result['num-records']) >= 1:
            sp_attr_info = result['attributes-list']['service-processor-network-info']
            return sp_attr_info.get_child_content('setup-status')
        return None

    def get_service_processor_network(self):
        """
        Return details about service processor network
        :param:
            name : name of the node
        :return: Details about service processor network. None if not found.
        :rtype: dict
        """
        spn_get_iter = netapp_utils.zapi.NaElement('service-processor-network-get-iter')
        query_info = {
            'query': {
                'service-processor-network-info': {
                    'node': self.parameters['node']
                }
            }
        }
        spn_get_iter.translate_struct(query_info)
        result = self.server.invoke_successfully(spn_get_iter, True)
        sp_details = None
        # check if job exists
        if int(result['num-records']) >= 1:
            sp_details = dict()
            sp_attr_info = result['attributes-list']['service-processor-network-info']
            for item_key, zapi_key in self.na_helper.zapi_string_keys.items():
                sp_details[item_key] = sp_attr_info.get_child_content(zapi_key)
            for item_key, zapi_key in self.na_helper.zapi_bool_keys.items():
                sp_details[item_key] = self.na_helper.get_value_for_bool(from_zapi=True,
                                                                         value=sp_attr_info.get_child_content(zapi_key))
            for item_key, zapi_key in self.na_helper.zapi_int_keys.items():
                sp_details[item_key] = self.na_helper.get_value_for_int(from_zapi=True,
                                                                        value=sp_attr_info.get_child_content(zapi_key))
        return sp_details

    def modify_service_processor_network(self, params=None):
        """
        Modify a service processor network.
        :param params: A dict of modified options.
        When dhcp is not set to v4, ip_address, netmask, and gateway_ip_address must be specified even if remains the same.
        """
        if self.parameters['is_enabled'] is False:
            if params.get('is_enabled') and len(params) > 1:
                self.module.fail_json(msg='Error: Cannot modify any other parameter for a service processor network if option "is_enabled" is set to false.')
            elif params.get('is_enabled') is None and len(params) > 0:
                self.module.fail_json(msg='Error: Cannot modify a service processor network if it is disabled.')

        sp_modify = netapp_utils.zapi.NaElement('service-processor-network-modify')
        sp_modify.add_new_child("node", self.parameters['node'])
        sp_modify.add_new_child("address-type", self.parameters['address_type'])
        sp_attributes = dict()
        for item_key in self.parameters:
            if item_key in self.na_helper.zapi_string_keys:
                zapi_key = self.na_helper.zapi_string_keys.get(item_key)
                sp_attributes[zapi_key] = self.parameters[item_key]
            elif item_key in self.na_helper.zapi_bool_keys:
                zapi_key = self.na_helper.zapi_bool_keys.get(item_key)
                sp_attributes[zapi_key] = self.na_helper.get_value_for_bool(from_zapi=False, value=self.parameters[item_key])
            elif item_key in self.na_helper.zapi_int_keys:
                zapi_key = self.na_helper.zapi_int_keys.get(item_key)
                sp_attributes[zapi_key] = self.na_helper.get_value_for_int(from_zapi=False, value=self.parameters[item_key])
        sp_modify.translate_struct(sp_attributes)
        try:
            self.server.invoke_successfully(sp_modify, enable_tunneling=True)
            if self.parameters.get('wait_for_completion'):
                retries = 10
                while self.get_sp_network_status() == 'in_progress' and retries > 0:
                    time.sleep(10)
                    retries = retries - 1
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error modifying service processor network: %s' % (to_native(error)),
                                  exception=traceback.format_exc())

    def autosupport_log(self):
        results = netapp_utils.get_cserver(self.server)
        cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
        netapp_utils.ems_log_event("na_ontap_service_processor_network", cserver)

    def apply(self):
        """
        Run Module based on play book
        """
        self.autosupport_log()
        current = self.get_service_processor_network()
        modify = self.na_helper.get_modified_attributes(current, self.parameters)
        if not current:
            self.module.fail_json(msg='Error No Service Processor for node: %s' % self.parameters['node'])
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                self.modify_service_processor_network(modify)
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppONTAPCifsSecurity(object):
    '''
    modify vserver cifs security
    '''
    def __init__(self):

        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(dict(
            vserver=dict(required=True, type='str'),
            kerberos_clock_skew=dict(required=False, type='int'),
            kerberos_ticket_age=dict(required=False, type='int'),
            kerberos_renew_age=dict(required=False, type='int'),
            kerberos_kdc_timeout=dict(required=False, type='int'),
            is_signing_required=dict(required=False, type='bool'),
            is_password_complexity_required=dict(required=False, type='bool'),
            is_aes_encryption_enabled=dict(required=False, type='bool'),
            is_smb_encryption_required=dict(required=False, type='bool'),
            lm_compatibility_level=dict(required=False, choices=['lm_ntlm_ntlmv2_krb', 'ntlm_ntlmv2_krb', 'ntlmv2_krb', 'krb']),
            referral_enabled_for_ad_ldap=dict(required=False, type='bool'),
            session_security_for_ad_ldap=dict(required=False, choices=['none', 'sign', 'seal']),
            smb1_enabled_for_dc_connections=dict(required=False, choices=['false', 'true', 'system_default']),
            smb2_enabled_for_dc_connections=dict(required=False, choices=['false', 'true', 'system_default']),
            use_start_tls_for_ad_ldap=dict(required=False, type='bool')
        ))

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

        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)
        self.set_playbook_zapi_key_map()

        if HAS_NETAPP_LIB is False:
            self.module.fail_json(msg="the python NetApp-Lib module is required")
        else:
            self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver'])

    def set_playbook_zapi_key_map(self):

        self.na_helper.zapi_int_keys = {
            'kerberos_clock_skew': 'kerberos-clock-skew',
            'kerberos_ticket_age': 'kerberos-ticket-age',
            'kerberos_renew_age': 'kerberos-renew-age',
            'kerberos_kdc_timeout': 'kerberos-kdc-timeout'
        }
        self.na_helper.zapi_bool_keys = {
            'is_signing_required': 'is-signing-required',
            'is_password_complexity_required': 'is-password-complexity-required',
            'is_aes_encryption_enabled': 'is-aes-encryption-enabled',
            'is_smb_encryption_required': 'is-smb-encryption-required',
            'referral_enabled_for_ad_ldap': 'referral-enabled-for-ad-ldap',
            'use_start_tls_for_ad_ldap': 'use-start-tls-for-ad-ldap'
        }
        self.na_helper.zapi_str_keys = {
            'lm_compatibility_level': 'lm-compatibility-level',
            'session_security_for_ad_ldap': 'session-security-for-ad-ldap',
            'smb1_enabled_for_dc_connections': 'smb1-enabled-for-dc-connections',
            'smb2_enabled_for_dc_connections': 'smb2-enabled-for-dc-connections'
        }

    def cifs_security_get_iter(self):
        """
        get current vserver cifs security.
        :return: a dict of vserver cifs security
        """
        cifs_security_get = netapp_utils.zapi.NaElement('cifs-security-get-iter')
        query = netapp_utils.zapi.NaElement('query')
        cifs_security = netapp_utils.zapi.NaElement('cifs-security')
        cifs_security.add_new_child('vserver', self.parameters['vserver'])
        query.add_child_elem(cifs_security)
        cifs_security_get.add_child_elem(query)
        cifs_security_details = dict()
        try:
            result = self.server.invoke_successfully(cifs_security_get, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error fetching cifs security from %s: %s'
                                      % (self.parameters['vserver'], to_native(error)),
                                  exception=traceback.format_exc())
        if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) > 0:
            cifs_security_info = result.get_child_by_name('attributes-list').get_child_by_name('cifs-security')
            for option, zapi_key in self.na_helper.zapi_int_keys.items():
                cifs_security_details[option] = self.na_helper.get_value_for_int(from_zapi=True, value=cifs_security_info.get_child_content(zapi_key))
            for option, zapi_key in self.na_helper.zapi_bool_keys.items():
                cifs_security_details[option] = self.na_helper.get_value_for_bool(from_zapi=True, value=cifs_security_info.get_child_content(zapi_key))
            for option, zapi_key in self.na_helper.zapi_str_keys.items():
                if cifs_security_info.get_child_content(zapi_key) is None:
                    cifs_security_details[option] = None
                else:
                    cifs_security_details[option] = cifs_security_info.get_child_content(zapi_key)
            return cifs_security_details
        return None

    def cifs_security_modify(self, modify):
        """
        :param modify: A list of attributes to modify
        :return: None
        """
        cifs_security_modify = netapp_utils.zapi.NaElement('cifs-security-modify')
        for attribute in modify:
            cifs_security_modify.add_new_child(self.attribute_to_name(attribute), str(self.parameters[attribute]))
        try:
            self.server.invoke_successfully(cifs_security_modify, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as e:
            self.module.fail_json(msg='Error modifying cifs security on %s: %s'
                                  % (self.parameters['vserver'], to_native(e)),
                                  exception=traceback.format_exc())

    @staticmethod
    def attribute_to_name(attribute):
        return str.replace(attribute, '_', '-')

    def apply(self):
        """Call modify operations."""
        self.asup_log_for_cserver("na_ontap_vserver_cifs_security")
        current = self.cifs_security_get_iter()
        modify = self.na_helper.get_modified_attributes(current, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if modify:
                    self.cifs_security_modify(modify)
        self.module.exit_json(changed=self.na_helper.changed)

    def asup_log_for_cserver(self, event_name):
        """
        Fetch admin vserver for the given cluster
        Create and Autosupport log event with the given module name
        :param event_name: Name of the event log
        :return: None
        """
        results = netapp_utils.get_cserver(self.server)
        cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
        netapp_utils.ems_log_event(event_name, cserver)
Exemplo n.º 3
0
class NetAppontapExportRule(object):
    ''' object initialize and class 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'),
                name=dict(required=True, type='str', aliases=['policy_name']),
                protocol=dict(required=False,
                              type='list',
                              elements='str',
                              default=None,
                              choices=[
                                  'any', 'nfs', 'nfs3', 'nfs4', 'cifs',
                                  'flexcache'
                              ]),
                client_match=dict(required=False, type='list', elements='str'),
                ro_rule=dict(required=False,
                             type='list',
                             elements='str',
                             default=None,
                             choices=[
                                 'any', 'none', 'never', 'krb5', 'krb5i',
                                 'krb5p', 'ntlm', 'sys'
                             ]),
                rw_rule=dict(required=False,
                             type='list',
                             elements='str',
                             default=None,
                             choices=[
                                 'any', 'none', 'never', 'krb5', 'krb5i',
                                 'krb5p', 'ntlm', 'sys'
                             ]),
                super_user_security=dict(required=False,
                                         type='list',
                                         elements='str',
                                         default=None,
                                         choices=[
                                             'any', 'none', 'never', 'krb5',
                                             'krb5i', 'krb5p', 'ntlm', 'sys'
                                         ]),
                allow_suid=dict(required=False, type='bool'),
                rule_index=dict(required=False, type='int'),
                anonymous_user_id=dict(required=False, type='int'),
                vserver=dict(required=True, type='str'),
            ))

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

        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)
        self.set_playbook_zapi_key_map()

        if HAS_NETAPP_LIB is False:
            self.module.fail_json(
                msg="the python NetApp-Lib module is required")
        else:
            self.server = netapp_utils.setup_na_ontap_zapi(
                module=self.module, vserver=self.parameters['vserver'])

    def set_playbook_zapi_key_map(self):
        self.na_helper.zapi_string_keys = {
            'client_match': 'client-match',
            'name': 'policy-name'
        }
        self.na_helper.zapi_list_keys = {
            'protocol': ('protocol', 'access-protocol'),
            'ro_rule': ('ro-rule', 'security-flavor'),
            'rw_rule': ('rw-rule', 'security-flavor'),
            'super_user_security': ('super-user-security', 'security-flavor'),
        }
        self.na_helper.zapi_bool_keys = {
            'allow_suid': 'is-allow-set-uid-enabled'
        }
        self.na_helper.zapi_int_keys = {
            'rule_index': 'rule-index',
            'anonymous_user_id': 'anonymous-user-id'
        }

    def set_query_parameters(self):
        """
        Return dictionary of query parameters and
        :return:
        """
        query = {
            'policy-name': self.parameters['name'],
            'vserver': self.parameters['vserver']
        }

        if self.parameters.get('rule_index'):
            query['rule-index'] = self.parameters['rule_index']
        elif self.parameters.get('client_match'):
            query['client-match'] = self.parameters['client_match']
        else:
            self.module.fail_json(
                msg=
                "Need to specify at least one of the rule_index and client_match option."
            )

        attributes = {'query': {'export-rule-info': query}}
        return attributes

    def get_export_policy_rule(self):
        """
        Return details about the export policy rule
        :param:
            name : Name of the export_policy
        :return: Details about the export_policy. None if not found.
        :rtype: dict
        """
        current, result = None, None
        rule_iter = netapp_utils.zapi.NaElement('export-rule-get-iter')
        rule_iter.translate_struct(self.set_query_parameters())
        try:
            result = self.server.invoke_successfully(rule_iter, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error getting export policy rule %s: %s' %
                (self.parameters['name'], to_native(error)),
                exception=traceback.format_exc())
        if result is not None and \
                result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
            current = dict()
            rule_info = result.get_child_by_name(
                'attributes-list').get_child_by_name('export-rule-info')
            for item_key, zapi_key in self.na_helper.zapi_string_keys.items():
                current[item_key] = rule_info.get_child_content(zapi_key)
            for item_key, zapi_key in self.na_helper.zapi_bool_keys.items():
                current[item_key] = self.na_helper.get_value_for_bool(
                    from_zapi=True, value=rule_info[zapi_key])
            for item_key, zapi_key in self.na_helper.zapi_int_keys.items():
                current[item_key] = self.na_helper.get_value_for_int(
                    from_zapi=True, value=rule_info[zapi_key])
            for item_key, zapi_key in self.na_helper.zapi_list_keys.items():
                parent, dummy = zapi_key
                current[item_key] = self.na_helper.get_value_for_list(
                    from_zapi=True,
                    zapi_parent=rule_info.get_child_by_name(parent))
            current['num_records'] = int(
                result.get_child_content('num-records'))
            if not self.parameters.get('rule_index'):
                self.parameters['rule_index'] = current['rule_index']
        return current

    def get_export_policy(self):
        """
        Return details about the export-policy
        :param:
            name : Name of the export-policy

        :return: Details about the export-policy. None if not found.
        :rtype: dict
        """
        export_policy_iter = netapp_utils.zapi.NaElement(
            'export-policy-get-iter')
        attributes = {
            'query': {
                'export-policy-info': {
                    'policy-name': self.parameters['name'],
                    'vserver': self.parameters['vserver']
                }
            }
        }

        export_policy_iter.translate_struct(attributes)
        try:
            result = self.server.invoke_successfully(export_policy_iter, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error getting export policy %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

        if result.get_child_by_name('num-records') and int(
                result.get_child_content('num-records')) == 1:
            return result

        return None

    def add_parameters_for_create_or_modify(self, na_element_object, values):
        """
            Add children node for create or modify NaElement object
            :param na_element_object: modify or create NaElement object
            :param values: dictionary of cron values to be added
            :return: None
        """
        for key in values:
            if key in self.na_helper.zapi_string_keys:
                zapi_key = self.na_helper.zapi_string_keys.get(key)
                na_element_object[zapi_key] = values[key]
            elif key in self.na_helper.zapi_list_keys:
                parent_key, child_key = self.na_helper.zapi_list_keys.get(key)
                na_element_object.add_child_elem(
                    self.na_helper.get_value_for_list(from_zapi=False,
                                                      zapi_parent=parent_key,
                                                      zapi_child=child_key,
                                                      data=values[key]))
            elif key in self.na_helper.zapi_int_keys:
                zapi_key = self.na_helper.zapi_int_keys.get(key)
                na_element_object[zapi_key] = self.na_helper.get_value_for_int(
                    from_zapi=False, value=values[key])
            elif key in self.na_helper.zapi_bool_keys:
                zapi_key = self.na_helper.zapi_bool_keys.get(key)
                na_element_object[
                    zapi_key] = self.na_helper.get_value_for_bool(
                        from_zapi=False, value=values[key])

    def create_export_policy_rule(self):
        """
        create rule for the export policy.
        """
        for key in ['client_match', 'ro_rule', 'rw_rule']:
            if self.parameters.get(key) is None:
                self.module.fail_json(
                    msg=
                    'Error: Missing required param for creating export policy rule %s'
                    % key)
        export_rule_create = netapp_utils.zapi.NaElement('export-rule-create')
        self.add_parameters_for_create_or_modify(export_rule_create,
                                                 self.parameters)
        try:
            self.server.invoke_successfully(export_rule_create,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error creating export policy rule %s: %s' %
                (self.parameters['name'], to_native(error)),
                exception=traceback.format_exc())

    def create_export_policy(self):
        """
        Creates an export policy
        """
        export_policy_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'export-policy-create', **{'policy-name': self.parameters['name']})
        try:
            self.server.invoke_successfully(export_policy_create,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error creating export-policy %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def delete_export_policy_rule(self, rule_index):
        """
        delete rule for the export policy.
        """
        export_rule_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'export-rule-destroy', **{
                'policy-name': self.parameters['name'],
                'rule-index': str(rule_index)
            })

        try:
            self.server.invoke_successfully(export_rule_delete,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error deleting export policy rule %s: %s' %
                (self.parameters['name'], to_native(error)),
                exception=traceback.format_exc())

    def modify_export_policy_rule(self, params):
        '''
        Modify an existing export policy rule
        :param params: dict() of attributes with desired values
        :return: None
        '''
        export_rule_modify = netapp_utils.zapi.NaElement.create_node_with_children(
            'export-rule-modify', **{
                'policy-name': self.parameters['name'],
                'rule-index': str(self.parameters['rule_index'])
            })
        self.add_parameters_for_create_or_modify(export_rule_modify, params)
        try:
            self.server.invoke_successfully(export_rule_modify,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error modifying allow_suid %s: %s' %
                (self.parameters['allow_suid'], to_native(error)),
                exception=traceback.format_exc())

    def autosupport_log(self):
        netapp_utils.ems_log_event("na_ontap_export_policy_rules", self.server)

    def apply(self):
        ''' Apply required action from the play'''
        self.autosupport_log()
        # convert client_match list to comma-separated string
        if self.parameters.get('client_match') is not None:
            self.parameters['client_match'] = ','.join(
                self.parameters['client_match'])
            self.parameters['client_match'] = self.parameters[
                'client_match'].replace(' ', '')

        current, modify = self.get_export_policy_rule(), None
        action = self.na_helper.get_cd_action(current, self.parameters)
        if action is None and self.parameters['state'] == 'present':
            modify = self.na_helper.get_modified_attributes(
                current, self.parameters)

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                # create export policy (if policy doesn't exist) only when changed=True
                if action == 'create':
                    if not self.get_export_policy():
                        self.create_export_policy()
                    self.create_export_policy_rule()
                elif action == 'delete':
                    if current['num_records'] > 1:
                        self.module.fail_json(
                            msg='Multiple export policy rules exist.'
                            'Please specify a rule_index to delete')
                    self.delete_export_policy_rule(current['rule_index'])
                elif modify:
                    self.modify_export_policy_rule(modify)
        self.module.exit_json(changed=self.na_helper.changed)
Exemplo n.º 4
0
class NetAppOntapLogForward(object):
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(choices=['present', 'absent'], default='present'),
                 destination=dict(required=True, type='str'),
                 port=dict(required=True, type='int'),
                 facility=dict(required=False,
                               type='str',
                               choices=[
                                   'kern', 'user', 'local0', 'local1',
                                   'local2', 'local3', 'local4', 'local5',
                                   'local6', 'local7'
                               ]),
                 force=dict(required=False, type='bool'),
                 protocol=dict(required=False,
                               type='str',
                               choices=[
                                   'udp_unencrypted', 'tcp_unencrypted',
                                   'tcp_encrypted'
                               ]),
                 verify_server=dict(required=False, type='bool')))
        self.module = AnsibleModule(argument_spec=self.argument_spec,
                                    supports_check_mode=True)

        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)

        self.rest_api = OntapRestAPI(self.module)
        self.use_rest = self.rest_api.is_rest()

        if not self.use_rest:
            if HAS_NETAPP_LIB is False:
                self.module.fail_json(
                    msg="the python NetApp-Lib module is required")
            else:
                self.server = netapp_utils.setup_na_ontap_zapi(
                    module=self.module)

    def get_log_forward_config(self):
        """
        gets log forward configuration
        :return: dict of log forward properties if exist, None if not
        """

        if self.use_rest:
            log_forward_config = None
            api = "security/audit/destinations"
            query = {
                'fields': 'port,protocol,facility,address,verify_server',
                'address': self.parameters['destination'],
                'port': self.parameters['port']
            }

            message, error = self.rest_api.get(api, query)
            if error:
                self.module.fail_json(msg=error)
            if len(message.keys()) == 0:
                return None
            elif 'records' in message and len(message['records']) == 0:
                return None
            elif 'records' not in message:
                error = "Unexpected response in get_security_key_manager from %s: %s" % (
                    api, repr(message))
                self.module.fail_json(msg=error)
            log_forward_config = {
                'destination': message['records'][0]['address'],
                'facility': message['records'][0]['facility'],
                'port': message['records'][0]['port'],
                'protocol': message['records'][0]['protocol'],
                'verify_server': message['records'][0]['verify_server']
            }

            return log_forward_config

        else:
            log_forward_config = None

            log_forward_get = netapp_utils.zapi.NaElement(
                'cluster-log-forward-get')
            log_forward_get.add_new_child('destination',
                                          self.parameters['destination'])
            log_forward_get.add_new_child(
                'port',
                self.na_helper.get_value_for_int(False,
                                                 self.parameters['port']))

            try:
                result = self.server.invoke_successfully(log_forward_get, True)
            except netapp_utils.zapi.NaApiError as error:
                if to_native(error.code) == "15661":
                    # config doesnt exist
                    return None
                else:
                    self.module.fail_json(
                        msg=
                        'Error getting log forward configuration for destination %s on port %s: %s'
                        % (self.parameters['destination'],
                           self.na_helper.get_value_for_int(
                               False,
                               self.parameters['port']), to_native(error)),
                        exception=traceback.format_exc())

            if result.get_child_by_name('attributes'):
                log_forward_attributes = result.get_child_by_name('attributes')
                cluster_log_forward_info = log_forward_attributes.get_child_by_name(
                    'cluster-log-forward-info')
                log_forward_config = {
                    'destination':
                    cluster_log_forward_info.get_child_content('destination'),
                    'facility':
                    cluster_log_forward_info.get_child_content('facility'),
                    'port':
                    self.na_helper.get_value_for_int(
                        True,
                        cluster_log_forward_info.get_child_content('port')),
                    'protocol':
                    cluster_log_forward_info.get_child_content('protocol'),
                    'verify_server':
                    self.na_helper.get_value_for_bool(
                        True,
                        cluster_log_forward_info.get_child_content(
                            'verify-server'))
                }

            return log_forward_config

    def create_log_forward_config(self):
        """
        Creates a log forward config
        :return: nothing
        """

        if self.use_rest:
            api = "security/audit/destinations"
            body = dict()
            body['address'] = self.parameters['destination']
            body['port'] = self.parameters['port']

            for attr in ('protocol', 'facility', 'verify_server', 'force'):
                if attr in self.parameters:
                    body[attr] = self.parameters[attr]

            dummy, error = self.rest_api.post(api, body)
            if error:
                self.module.fail_json(msg=error)

        else:
            log_forward_config_obj = netapp_utils.zapi.NaElement(
                'cluster-log-forward-create')
            log_forward_config_obj.add_new_child(
                'destination', self.parameters['destination'])
            log_forward_config_obj.add_new_child(
                'port',
                self.na_helper.get_value_for_int(False,
                                                 self.parameters['port']))

            if 'facility' in self.parameters:
                log_forward_config_obj.add_new_child(
                    'facility', self.parameters['facility'])

            if 'force' in self.parameters:
                log_forward_config_obj.add_new_child(
                    'force',
                    self.na_helper.get_value_for_bool(
                        False, self.parameters['force']))

            if 'protocol' in self.parameters:
                log_forward_config_obj.add_new_child(
                    'protocol', self.parameters['protocol'])

            if 'verify_server' in self.parameters:
                log_forward_config_obj.add_new_child(
                    'verify-server',
                    self.na_helper.get_value_for_bool(
                        False, self.parameters['verify_server']))

            try:
                self.server.invoke_successfully(log_forward_config_obj, True)
            except netapp_utils.zapi.NaApiError as error:
                self.module.fail_json(
                    msg=
                    'Error creating log forward config with destination %s on port %s: %s'
                    % (self.parameters['destination'],
                       self.na_helper.get_value_for_int(
                           False, self.parameters['port']), to_native(error)),
                    exception=traceback.format_exc())

    def modify_log_forward_config(self):
        # need to recreate as protocol can't be changed
        self.destroy_log_forward_config()
        self.create_log_forward_config()

    def destroy_log_forward_config(self):
        """
        Delete a log forward configuration
        :return: nothing
        """
        if self.use_rest:

            api = "security/audit/destinations/%s/%s" % (
                self.parameters['destination'], self.parameters['port'])
            body = None
            query = {'return_timeout': 3}
            dummy, error = self.rest_api.delete(api, body, query)
            if error:
                self.module.fail_json(msg=error)

        else:
            log_forward_config_obj = netapp_utils.zapi.NaElement(
                'cluster-log-forward-destroy')
            log_forward_config_obj.add_new_child(
                'destination', self.parameters['destination'])
            log_forward_config_obj.add_new_child(
                'port',
                self.na_helper.get_value_for_int(False,
                                                 self.parameters['port']))

            try:
                self.server.invoke_successfully(log_forward_config_obj, True)
            except netapp_utils.zapi.NaApiError as error:
                self.module.fail_json(
                    msg=
                    'Error destroying log forward destination %s on port %s: %s'
                    % (self.parameters['destination'],
                       self.na_helper.get_value_for_int(
                           False, self.parameters['port']), to_native(error)),
                    exception=traceback.format_exc())

    def ems_log_event(self):
        results = netapp_utils.get_cserver(self.server)
        cserver = netapp_utils.setup_na_ontap_zapi(module=self.module,
                                                   vserver=results)
        return netapp_utils.ems_log_event("na_ontap_log_forward", cserver)

    def apply(self):
        if not self.use_rest:
            self.ems_log_event()

        current = self.get_log_forward_config()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)

        if cd_action is None and self.parameters['state'] == 'present':
            modify = self.na_helper.get_modified_attributes(
                current, self.parameters)

        if self.na_helper.changed:
            if not self.module.check_mode:
                if cd_action == 'create':
                    self.create_log_forward_config()
                elif cd_action == 'delete':
                    self.destroy_log_forward_config()
                elif modify:
                    self.modify_log_forward_config()

        self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapEfficiencyPolicy(object):
    """
    Create, delete and modify efficiency policy
    """
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 policy_name=dict(required=True, type='str'),
                 comment=dict(required=False, type='str'),
                 duration=dict(required=False, type='int'),
                 enabled=dict(required=False, type='bool'),
                 policy_type=dict(required=False,
                                  choices=['threshold', 'scheduled']),
                 qos_policy=dict(required=False,
                                 choices=['background', 'best_effort']),
                 schedule=dict(reuired=False, type='str'),
                 vserver=dict(required=True, type='str'),
                 changelog_threshold_percent=dict(required=False, type='int')))
        self.module = AnsibleModule(
            argument_spec=self.argument_spec,
            supports_check_mode=True,
            mutually_exclusive=[('changelog_threshold_percent', 'duration'),
                                ('changelog_threshold_percent', 'schedule')])
        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)
        self.set_playbook_zapi_key_map()
        if self.parameters.get('policy_type'):
            if self.parameters['policy_type'] == 'threshold':
                if self.parameters.get('duration'):
                    self.module.fail_json(
                        msg="duration cannot be set if policy_type is threshold"
                    )
                if self.parameters.get('schedule'):
                    self.module.fail_json(
                        msg='schedule cannot be set if policy_type is threshold'
                    )
            # if policy_type is 'scheduled'
            else:
                if self.parameters.get('changelog_threshold_percent'):
                    self.module.fail_json(
                        msg=
                        'changelog_threshold_percent cannot be set if policy_type is scheduled'
                    )
        if HAS_NETAPP_LIB is False:
            self.module.fail_json(
                msg="the python NetApp-Lib module is required")
        else:
            self.server = netapp_utils.setup_na_ontap_zapi(
                module=self.module, vserver=self.parameters['vserver'])

    def set_playbook_zapi_key_map(self):

        self.na_helper.zapi_int_keys = {
            'changelog_threshold_percent': 'changelog-threshold-percent',
            'duration': 'duration',
        }
        self.na_helper.zapi_str_keys = {
            'policy_name': 'policy-name',
            'comment': 'comment',
            'policy_type': 'policy-type',
            'qos_policy': 'qos-policy',
            'schedule': 'schedule'
        }
        self.na_helper.zapi_bool_keys = {'enabled': 'enabled'}

    def get_efficiency_policy(self):
        """
        Get a efficiency policy
        :return: a efficiency-policy info
        """
        sis_policy_obj = netapp_utils.zapi.NaElement("sis-policy-get-iter")
        query = netapp_utils.zapi.NaElement("query")
        sis_policy_info = netapp_utils.zapi.NaElement("sis-policy-info")
        sis_policy_info.add_new_child("policy-name",
                                      self.parameters['policy_name'])
        sis_policy_info.add_new_child("vserver", self.parameters['vserver'])
        query.add_child_elem(sis_policy_info)
        sis_policy_obj.add_child_elem(query)
        try:
            results = self.server.invoke_successfully(sis_policy_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg="Error searching for efficiency policy %s: %s" %
                (self.parameters['policy_name'], to_native(error)),
                exception=traceback.format_exc())
        return_value = {}
        if results.get_child_by_name('num-records') and int(
                results.get_child_content('num-records')) == 1:
            attributes_list = results.get_child_by_name('attributes-list')
            sis_info = attributes_list.get_child_by_name('sis-policy-info')
            for option, zapi_key in self.na_helper.zapi_int_keys.items():
                return_value[option] = self.na_helper.get_value_for_int(
                    from_zapi=True, value=sis_info.get_child_content(zapi_key))
            for option, zapi_key in self.na_helper.zapi_bool_keys.items():
                return_value[option] = self.na_helper.get_value_for_bool(
                    from_zapi=True, value=sis_info.get_child_content(zapi_key))
            for option, zapi_key in self.na_helper.zapi_str_keys.items():
                return_value[option] = sis_info.get_child_content(zapi_key)
            return return_value
        return None

    def create_efficiency_policy(self):
        """
        Creates a efficiency policy
        :return: None
        """
        sis_policy_obj = netapp_utils.zapi.NaElement("sis-policy-create")
        for option, zapi_key in self.na_helper.zapi_int_keys.items():
            if self.parameters.get(option):
                sis_policy_obj.add_new_child(
                    zapi_key,
                    self.na_helper.get_value_for_int(
                        from_zapi=False, value=self.parameters[option]))
        for option, zapi_key in self.na_helper.zapi_bool_keys.items():
            if self.parameters.get(option):
                sis_policy_obj.add_new_child(
                    zapi_key,
                    self.na_helper.get_value_for_bool(
                        from_zapi=False, value=self.parameters[option]))
        for option, zapi_key in self.na_helper.zapi_str_keys.items():
            if self.parameters.get(option):
                sis_policy_obj.add_new_child(zapi_key,
                                             str(self.parameters[option]))
        try:
            self.server.invoke_successfully(sis_policy_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg="Error creating efficiency policy %s: %s" %
                (self.parameters["policy_name"], to_native(error)),
                exception=traceback.format_exc())

    def delete_efficiency_policy(self):
        """
        Delete a efficiency Policy
        :return: None
        """
        sis_policy_obj = netapp_utils.zapi.NaElement("sis-policy-delete")
        sis_policy_obj.add_new_child("policy-name",
                                     self.parameters['policy_name'])
        try:
            self.server.invoke_successfully(sis_policy_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg="Error deleting efficiency policy %s: %s" %
                (self.parameters["policy_name"], to_native(error)),
                exception=traceback.format_exc())

    def modify_efficiency_policy(self, current, modify):
        """
        Modify a efficiency policy
        :return: None
        """
        sis_policy_obj = netapp_utils.zapi.NaElement("sis-policy-modify")
        sis_policy_obj.add_new_child("policy-name",
                                     self.parameters['policy_name'])
        # sis-policy-create zapi pre-checks the options and fails if it's not supported.
        # sis-policy-modify pre-checks one of the options, but tries to modify the others even it's not supported. And it will mess up the vsim.
        # Do the checks before sending to the zapi.
        if current['policy_type'] == 'scheduled' and self.parameters.get(
                'policy_type') != 'threshold':
            if modify.get('changelog_threshold_percent'):
                self.module.fail_json(
                    msg=
                    "changelog_threshold_percent cannot be set if policy_type is scheduled"
                )
        elif current['policy_type'] == 'threshold' and self.parameters.get(
                'policy_type') != 'scheduled':
            if modify.get('duration'):
                self.module.fail_json(
                    msg="duration cannot be set if policy_type is threshold")
            elif modify.get('schedule'):
                self.module.fail_json(
                    msg="schedule cannot be set if policy_type is threshold")
        for attribute in modify:
            sis_policy_obj.add_new_child(self.attribute_to_name(attribute),
                                         str(self.parameters[attribute]))
        try:
            self.server.invoke_successfully(sis_policy_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg="Error modifying efficiency policy %s: %s" %
                (self.parameters["policy_name"], to_native(error)),
                exception=traceback.format_exc())

    @staticmethod
    def attribute_to_name(attribute):
        return str.replace(attribute, '_', '-')

    def apply(self):
        netapp_utils.ems_log_event("na_ontap_efficiency_policy", self.server)
        current = self.get_efficiency_policy()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if cd_action is None and self.parameters['state'] == 'present':
            modify = self.na_helper.get_modified_attributes(
                current, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_efficiency_policy()
                elif cd_action == 'delete':
                    self.delete_efficiency_policy()
                elif modify:
                    self.modify_efficiency_policy(current, modify)
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapLDAPClient(object):
    '''
    LDAP Client definition class
    '''
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(ad_domain=dict(required=False, default=None, type='str'),
                 base_dn=dict(required=False, type='str'),
                 base_scope=dict(required=False,
                                 default=None,
                                 choices=['subtree', 'onelevel', 'base']),
                 bind_as_cifs_server=dict(required=False, type='bool'),
                 bind_dn=dict(required=False, default=None, type='str'),
                 bind_password=dict(type='str',
                                    required=False,
                                    default=None,
                                    no_log=True),
                 name=dict(required=True, type='str'),
                 ldap_servers=dict(required=False, type='list',
                                   elements='str'),
                 min_bind_level=dict(required=False,
                                     default=None,
                                     choices=['anonymous', 'simple', 'sasl']),
                 preferred_ad_servers=dict(required=False,
                                           type='list',
                                           elements='str'),
                 port=dict(required=False, default=None, type='int'),
                 query_timeout=dict(required=False, default=None, type='int'),
                 referral_enabled=dict(required=False, type='bool'),
                 schema=dict(
                     required=False,
                     default=None,
                     choices=['AD-IDMU', 'AD-SFU', 'MS-AD-BIS', 'RFC-2307']),
                 session_security=dict(required=False,
                                       default=None,
                                       choices=['none', 'sign', 'seal']),
                 state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 use_start_tls=dict(required=False, type='bool'),
                 vserver=dict(required=True, type='str')))

        self.module = AnsibleModule(
            argument_spec=self.argument_spec,
            supports_check_mode=True,
            required_if=[
                ('state', 'present', ['schema']),
            ],
            mutually_exclusive=[['ldap_servers', 'ad_domain'],
                                ['ldap_servers', 'preferred_ad_servers']],
        )
        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)

        if HAS_NETAPP_LIB is False:
            self.module.fail_json(
                msg="the python NetApp-Lib module is required")
        else:
            self.server = netapp_utils.setup_na_ontap_zapi(
                module=self.module, vserver=self.parameters['vserver'])

        self.simple_attributes = [
            'ad_domain', 'base_dn', 'base_scope', 'bind_as_cifs_server',
            'bind_dn', 'bind_password', 'min_bind_level', 'port',
            'query_timeout', 'referral_enabled', 'session_security',
            'use_start_tls'
        ]

    def get_ldap_client(self, client_config_name=None, vserver_name=None):
        '''
        Checks if LDAP client config exists.

        :return:
            ldap client config object if found
            None if not found
        :rtype: object/None
        '''
        # Make query
        client_config_info = netapp_utils.zapi.NaElement(
            'ldap-client-get-iter')

        if client_config_name is None:
            client_config_name = self.parameters['name']

        if vserver_name is None:
            vserver_name = '*'

        query_details = netapp_utils.zapi.NaElement.create_node_with_children(
            'ldap-client', **{
                'ldap-client-config': client_config_name,
                'vserver': vserver_name
            })

        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(query_details)
        client_config_info.add_child_elem(query)

        result = self.server.invoke_successfully(client_config_info,
                                                 enable_tunneling=False)

        # Get LDAP client configuration details
        client_config_details = None
        if result.get_child_by_name('num-records') and int(
                result.get_child_content('num-records')) >= 1:
            attributes_list = result.get_child_by_name('attributes-list')
            client_config_info = attributes_list.get_child_by_name(
                'ldap-client')
            # Get LDAP servers list
            ldap_server_list = list()
            get_list = client_config_info.get_child_by_name('ldap-servers')
            if get_list is not None:
                ldap_server_list = [
                    x.get_content() for x in get_list.get_children()
                ]

            preferred_ad_servers_list = list()
            get_pref_ad_server_list = client_config_info.get_child_by_name(
                'preferred-ad-servers')
            if get_pref_ad_server_list is not None:
                preferred_ad_servers_list = [
                    x.get_content()
                    for x in get_pref_ad_server_list.get_children()
                ]

            # Define config details structure
            client_config_details = {
                'name':
                client_config_info.get_child_content('ldap-client-config'),
                'ldap_servers':
                ldap_server_list,
                'ad_domain':
                client_config_info.get_child_content('ad-domain'),
                'base_dn':
                client_config_info.get_child_content('base-dn'),
                'base_scope':
                client_config_info.get_child_content('base-scope'),
                'bind_as_cifs_server':
                self.na_helper.get_value_for_bool(
                    from_zapi=True,
                    value=client_config_info.get_child_content(
                        'bind-as-cifs-server')),
                'bind_dn':
                client_config_info.get_child_content('bind-dn'),
                'bind_password':
                client_config_info.get_child_content('bind-password'),
                'min_bind_level':
                client_config_info.get_child_content('min-bind-level'),
                'port':
                self.na_helper.get_value_for_int(
                    from_zapi=True,
                    value=client_config_info.get_child_content('port')),
                'preferred_ad_servers':
                preferred_ad_servers_list,
                'query_timeout':
                self.na_helper.get_value_for_int(
                    from_zapi=True,
                    value=client_config_info.get_child_content(
                        'query-timeout')),
                'referral_enabled':
                self.na_helper.get_value_for_bool(
                    from_zapi=True,
                    value=client_config_info.get_child_content(
                        'referral-enabled')),
                'schema':
                client_config_info.get_child_content('schema'),
                'session_security':
                client_config_info.get_child_content('session-security'),
                'use_start_tls':
                self.na_helper.get_value_for_bool(
                    from_zapi=True,
                    value=client_config_info.get_child_content(
                        'use-start-tls'))
            }
        return client_config_details

    def create_ldap_client(self):
        '''
        Create LDAP client configuration
        '''

        options = {
            'ldap-client-config': self.parameters['name'],
            'schema': self.parameters['schema'],
        }

        # Other options/attributes
        for attribute in self.simple_attributes:
            if self.parameters.get(attribute) is not None:
                options[str(attribute).replace('_', '-')] = str(
                    self.parameters[attribute])

        # Initialize NaElement
        ldap_client_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'ldap-client-create', **options)

        # LDAP servers NaElement
        if self.parameters.get('ldap_servers') is not None:
            ldap_servers_element = netapp_utils.zapi.NaElement('ldap-servers')
            for ldap_server_name in self.parameters['ldap_servers']:
                ldap_servers_element.add_new_child('string', ldap_server_name)
            ldap_client_create.add_child_elem(ldap_servers_element)

        # preferred_ad_servers
        if self.parameters.get('preferred_ad_servers') is not None:
            preferred_ad_servers_element = netapp_utils.zapi.NaElement(
                'preferred-ad-servers')
            for pref_ad_server in self.parameters['preferred_ad_servers']:
                preferred_ad_servers_element.add_new_child(
                    'ip-address', pref_ad_server)
            ldap_client_create.add_child_elem(preferred_ad_servers_element)

        # Try to create LDAP configuration
        try:
            self.server.invoke_successfully(ldap_client_create,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as errcatch:
            self.module.fail_json(
                msg='Error creating LDAP client %s: %s' %
                (self.parameters['name'], to_native(errcatch)),
                exception=traceback.format_exc())

    def delete_ldap_client(self):
        '''
        Delete LDAP client configuration
        '''
        ldap_client_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'ldap-client-delete',
            **{'ldap-client-config': self.parameters['name']})

        try:
            self.server.invoke_successfully(ldap_client_delete,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as errcatch:
            self.module.fail_json(
                msg='Error deleting LDAP client configuration %s: %s' %
                (self.parameters['name'], to_native(errcatch)),
                exception=traceback.format_exc())

    def modify_ldap_client(self, modify):
        '''
        Modify LDAP client
        :param modify: list of modify attributes
        '''
        ldap_client_modify = netapp_utils.zapi.NaElement('ldap-client-modify')
        ldap_client_modify.add_new_child('ldap-client-config',
                                         self.parameters['name'])

        for attribute in modify:
            # LDAP_servers
            if attribute == 'ldap_servers':
                ldap_servers_element = netapp_utils.zapi.NaElement(
                    'ldap-servers')
                for ldap_server_name in self.parameters['ldap_servers']:
                    ldap_servers_element.add_new_child('string',
                                                       ldap_server_name)
                ldap_client_modify.add_child_elem(ldap_servers_element)
            # preferred_ad_servers
            if attribute == 'preferred_ad_servers':
                preferred_ad_servers_element = netapp_utils.zapi.NaElement(
                    'preferred-ad-servers')
                ldap_client_modify.add_child_elem(preferred_ad_servers_element)
                for pref_ad_server in self.parameters['preferred_ad_servers']:
                    preferred_ad_servers_element.add_new_child(
                        'ip-address', pref_ad_server)
            # Simple attributes
            if attribute in self.simple_attributes:
                ldap_client_modify.add_new_child(
                    str(attribute).replace('_', '-'),
                    str(self.parameters[attribute]))

        # Try to modify LDAP client
        try:
            self.server.invoke_successfully(ldap_client_modify,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as errcatch:
            self.module.fail_json(
                msg='Error modifying LDAP client %s: %s' %
                (self.parameters['name'], to_native(errcatch)),
                exception=traceback.format_exc())

    def apply(self):
        '''Call create/modify/delete operations.'''
        current = self.get_ldap_client()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)

        # state is present, either ldap_servers or ad_domain is required
        if self.parameters['state'] == 'present' and not self.parameters.get('ldap_servers') \
                and self.parameters.get('ad_domain') is None:
            self.module.fail_json(
                msg='Required one of ldap_servers or ad_domain')

        if self.parameters['state'] == 'present' and cd_action is None:
            modify = self.na_helper.get_modified_attributes(
                current, self.parameters)

        # create an ems log event for users with auto support turned on
        netapp_utils.ems_log_event("na_ontap_ldap_client", self.server)

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_ldap_client()
                elif cd_action == 'delete':
                    self.delete_ldap_client()
                elif modify:
                    self.modify_ldap_client(modify)
        self.module.exit_json(changed=self.na_helper.changed)
Exemplo n.º 7
0
class NetAppOntapUnixGroup(object):
    """
    Common operations to manage UNIX groups
    """
    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'),
                 name=dict(required=True, type='str'),
                 id=dict(required=False, type='int'),
                 skip_name_validation=dict(required=False, type='bool'),
                 vserver=dict(required=True, type='str'),
                 users=dict(required=False, type='list', elements='str')))

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

        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)
        self.set_playbook_zapi_key_map()

        if HAS_NETAPP_LIB is False:
            self.module.fail_json(
                msg="the python NetApp-Lib module is required")
        else:
            self.server = netapp_utils.setup_na_ontap_zapi(
                module=self.module, vserver=self.parameters['vserver'])

    def set_playbook_zapi_key_map(self):
        self.na_helper.zapi_string_keys = {'name': 'group-name'}
        self.na_helper.zapi_int_keys = {'id': 'group-id'}
        self.na_helper.zapi_bool_keys = {
            'skip_name_validation': 'skip-name-validation'
        }

    def get_unix_group(self):
        """
        Checks if the UNIX group exists.

        :return:
            dict() if group found
            None if group is not found
        """

        get_unix_group = netapp_utils.zapi.NaElement(
            'name-mapping-unix-group-get-iter')
        attributes = {
            'query': {
                'unix-group-info': {
                    'group-name': self.parameters['name'],
                    'vserver': self.parameters['vserver'],
                }
            }
        }
        get_unix_group.translate_struct(attributes)
        try:
            result = self.server.invoke_successfully(get_unix_group,
                                                     enable_tunneling=True)
            if result.get_child_by_name('num-records') and int(
                    result.get_child_content('num-records')) >= 1:
                group_info = result['attributes-list']['unix-group-info']
                group_details = dict()
            else:
                return None
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error getting UNIX group %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())
        for item_key, zapi_key in self.na_helper.zapi_string_keys.items():
            group_details[item_key] = group_info[zapi_key]
        for item_key, zapi_key in self.na_helper.zapi_int_keys.items():
            group_details[item_key] = self.na_helper.get_value_for_int(
                from_zapi=True, value=group_info[zapi_key])
        if group_info.get_child_by_name('users') is not None:
            group_details['users'] = [
                user.get_child_content('user-name') for user in
                group_info.get_child_by_name('users').get_children()
            ]
        else:
            group_details['users'] = None
        return group_details

    def create_unix_group(self):
        """
        Creates an UNIX group in the specified Vserver

        :return: None
        """
        if self.parameters.get('id') is None:
            self.module.fail_json(
                msg='Error: Missing a required parameter for create: (id)')

        group_create = netapp_utils.zapi.NaElement(
            'name-mapping-unix-group-create')
        group_details = {}
        for item in self.parameters:
            if item in self.na_helper.zapi_string_keys:
                zapi_key = self.na_helper.zapi_string_keys.get(item)
                group_details[zapi_key] = self.parameters[item]
            elif item in self.na_helper.zapi_bool_keys:
                zapi_key = self.na_helper.zapi_bool_keys.get(item)
                group_details[zapi_key] = self.na_helper.get_value_for_bool(
                    from_zapi=False, value=self.parameters[item])
            elif item in self.na_helper.zapi_int_keys:
                zapi_key = self.na_helper.zapi_int_keys.get(item)
                group_details[zapi_key] = self.na_helper.get_value_for_int(
                    from_zapi=True, value=self.parameters[item])
        group_create.translate_struct(group_details)
        try:
            self.server.invoke_successfully(group_create,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error creating UNIX group %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())
        if self.parameters.get('users') is not None:
            self.modify_users_in_group()

    def delete_unix_group(self):
        """
        Deletes an UNIX group from a vserver

        :return: None
        """
        group_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'name-mapping-unix-group-destroy',
            **{'group-name': self.parameters['name']})

        try:
            self.server.invoke_successfully(group_delete,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error removing UNIX group %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def modify_unix_group(self, params):
        """
        Modify an UNIX group from a vserver
        :param params: modify parameters
        :return: None
        """
        # modify users requires separate zapi.
        if 'users' in params:
            self.modify_users_in_group()
            if len(params) == 1:
                return

        group_modify = netapp_utils.zapi.NaElement(
            'name-mapping-unix-group-modify')
        group_details = {'group-name': self.parameters['name']}
        for key in params:
            if key in self.na_helper.zapi_int_keys:
                zapi_key = self.na_helper.zapi_int_keys.get(key)
                group_details[zapi_key] = self.na_helper.get_value_for_int(
                    from_zapi=True, value=params[key])
        group_modify.translate_struct(group_details)

        try:
            self.server.invoke_successfully(group_modify,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error modifying UNIX group %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def modify_users_in_group(self):
        """
        Add/delete one or many users in a UNIX group

        :return: None
        """
        current_users = self.get_unix_group().get('users')
        expect_users = self.parameters.get('users')

        if current_users is None:
            current_users = []
        if expect_users[0] == '' and len(expect_users) == 1:
            expect_users = []

        users_to_remove = list(set(current_users) - set(expect_users))
        users_to_add = list(set(expect_users) - set(current_users))

        if len(users_to_add) > 0:
            for user in users_to_add:
                add_user = netapp_utils.zapi.NaElement(
                    'name-mapping-unix-group-add-user')
                group_details = {
                    'group-name': self.parameters['name'],
                    'user-name': user
                }
                add_user.translate_struct(group_details)
                try:
                    self.server.invoke_successfully(add_user,
                                                    enable_tunneling=True)
                except netapp_utils.zapi.NaApiError as error:
                    self.module.fail_json(
                        msg='Error adding user %s to UNIX group %s: %s' %
                        (user, self.parameters['name'], to_native(error)),
                        exception=traceback.format_exc())

        if len(users_to_remove) > 0:
            for user in users_to_remove:
                delete_user = netapp_utils.zapi.NaElement(
                    'name-mapping-unix-group-delete-user')
                group_details = {
                    'group-name': self.parameters['name'],
                    'user-name': user
                }
                delete_user.translate_struct(group_details)
                try:
                    self.server.invoke_successfully(delete_user,
                                                    enable_tunneling=True)
                except netapp_utils.zapi.NaApiError as error:
                    self.module.fail_json(
                        msg='Error deleting user %s from UNIX group %s: %s' %
                        (user, self.parameters['name'], to_native(error)),
                        exception=traceback.format_exc())

    def autosupport_log(self):
        """
          Autosupport log for unix_group
          :return: None
          """
        netapp_utils.ems_log_event("na_ontap_unix_group", self.server)

    def apply(self):
        """
        Invoke appropriate action based on playbook parameters

        :return: None
        """
        self.autosupport_log()
        current = self.get_unix_group()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if self.parameters['state'] == 'present' and cd_action is None:
            modify = self.na_helper.get_modified_attributes(
                current, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_unix_group()
                elif cd_action == 'delete':
                    self.delete_unix_group()
                else:
                    self.modify_unix_group(modify)
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppONTAPSoftwareUpdate(object):
    """
    Class with ONTAP software update 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'),
            nodes=dict(required=False, type='list', elements='str', aliases=["node"]),
            package_version=dict(required=True, type='str'),
            package_url=dict(required=True, type='str'),
            ignore_validation_warning=dict(required=False, type='bool', default=False),
            download_only=dict(required=False, type='bool', default=False),
            stabilize_minutes=dict(required=False, type='int'),
            timeout=dict(required=False, type='int', default=1800)
        ))

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

        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)

        if HAS_NETAPP_LIB is False:
            self.module.fail_json(msg="the python NetApp-Lib module is required")
        else:
            self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)

    def cluster_image_get_iter(self):
        """
        Compose NaElement object to query current version
        :return: NaElement object for cluster-image-get-iter with query
        """
        cluster_image_get = netapp_utils.zapi.NaElement('cluster-image-get-iter')
        query = netapp_utils.zapi.NaElement('query')
        cluster_image_info = netapp_utils.zapi.NaElement('cluster-image-info')
        query.add_child_elem(cluster_image_info)
        cluster_image_get.add_child_elem(query)
        return cluster_image_get

    def cluster_image_get(self):
        """
        Get current cluster image info
        :return: True if query successful, else return None
        """
        cluster_image_get_iter = self.cluster_image_get_iter()
        try:
            result = self.server.invoke_successfully(cluster_image_get_iter, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error fetching cluster image details: %s: %s'
                                      % (self.parameters['package_version'], to_native(error)),
                                  exception=traceback.format_exc())
        # return cluster image details
        if result.get_child_by_name('num-records') and \
                int(result.get_child_content('num-records')) > 0:
            return True
        return None

    def cluster_image_get_for_node(self, node_name):
        """
        Get current cluster image info for given node
        """
        cluster_image_get = netapp_utils.zapi.NaElement('cluster-image-get')
        cluster_image_get.add_new_child('node-id', node_name)
        try:
            self.server.invoke_successfully(cluster_image_get, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error fetching cluster image details for %s: %s'
                                      % (node_name, to_native(error)),
                                  exception=traceback.format_exc())

    @staticmethod
    def get_localname(tag):
        return netapp_utils.zapi.etree.QName(tag).localname

    def cluster_image_update_progress_get(self, ignore_connection_error=True):
        """
        Get current cluster image update progress info
        :return: Dictionary of cluster image update progress if query successful, else return None
        """
        cluster_update_progress_get = netapp_utils.zapi.NaElement('cluster-image-update-progress-info')
        cluster_update_progress_info = dict()
        try:
            result = self.server.invoke_successfully(cluster_update_progress_get, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            # return empty dict on error to satisfy package delete upon image update
            if ignore_connection_error:
                return cluster_update_progress_info
            self.module.fail_json(msg='Error fetching cluster image update progress details: %s' % (to_native(error)),
                                  exception=traceback.format_exc())
        # return cluster image update progress details
        if result.get_child_by_name('attributes').get_child_by_name('ndu-progress-info'):
            update_progress_info = result.get_child_by_name('attributes').get_child_by_name('ndu-progress-info')
            cluster_update_progress_info['overall_status'] = update_progress_info.get_child_content('overall-status')
            cluster_update_progress_info['completed_node_count'] = update_progress_info.\
                get_child_content('completed-node-count')
            reports = update_progress_info.get_child_by_name('validation-reports')
            if reports:
                cluster_update_progress_info['validation_reports'] = list()
                for report in reports.get_children():
                    checks = dict()
                    for check in report.get_children():
                        checks[self.get_localname(check.get_name())] = check.get_content()
                    cluster_update_progress_info['validation_reports'].append(checks)
        return cluster_update_progress_info

    def cluster_image_update(self):
        """
        Update current cluster image
        """
        cluster_update_info = netapp_utils.zapi.NaElement('cluster-image-update')
        cluster_update_info.add_new_child('package-version', self.parameters['package_version'])
        cluster_update_info.add_new_child('ignore-validation-warning',
                                          str(self.parameters['ignore_validation_warning']))
        if self.parameters.get('stabilize_minutes'):
            cluster_update_info.add_new_child('stabilize-minutes',
                                              self.na_helper.get_value_for_int(False, self.parameters['stabilize_minutes']))
        if self.parameters.get('nodes'):
            cluster_nodes = netapp_utils.zapi.NaElement('nodes')
            for node in self.parameters['nodes']:
                cluster_nodes.add_new_child('node-name', node)
            cluster_update_info.add_child_elem(cluster_nodes)
        try:
            self.server.invoke_successfully(cluster_update_info, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            msg = 'Error updating cluster image for %s: %s' % (self.parameters['package_version'], to_native(error))
            cluster_update_progress_info = self.cluster_image_update_progress_get(ignore_connection_error=True)
            validation_reports = str(cluster_update_progress_info.get('validation_reports'))
            self.module.fail_json(msg=msg, validation_reports=validation_reports, exception=traceback.format_exc())

    def cluster_image_package_download(self):
        """
        Get current cluster image package download
        :return: True if package already exists, else return False
        """
        cluster_image_package_download_info = netapp_utils.zapi.NaElement('cluster-image-package-download')
        cluster_image_package_download_info.add_new_child('package-url', self.parameters['package_url'])
        try:
            self.server.invoke_successfully(cluster_image_package_download_info, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            # Error 18408 denotes Package image with the same name already exists
            if to_native(error.code) == "18408":
                return True
            else:
                self.module.fail_json(msg='Error downloading cluster image package for %s: %s'
                                          % (self.parameters['package_url'], to_native(error)),
                                      exception=traceback.format_exc())
        return False

    def cluster_image_package_delete(self):
        """
        Delete current cluster image package
        """
        cluster_image_package_delete_info = netapp_utils.zapi.NaElement('cluster-image-package-delete')
        cluster_image_package_delete_info.add_new_child('package-version', self.parameters['package_version'])
        try:
            self.server.invoke_successfully(cluster_image_package_delete_info, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error deleting cluster image package for %s: %s'
                                      % (self.parameters['package_version'], to_native(error)),
                                  exception=traceback.format_exc())

    def cluster_image_package_download_progress(self):
        """
        Get current cluster image package download progress
        :return: Dictionary of cluster image download progress if query successful, else return None
        """
        cluster_image_package_download_progress_info = netapp_utils.zapi.\
            NaElement('cluster-image-get-download-progress')
        try:
            result = self.server.invoke_successfully(
                cluster_image_package_download_progress_info, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error fetching cluster image package download progress for %s: %s'
                                      % (self.parameters['package_url'], to_native(error)),
                                  exception=traceback.format_exc())
        # return cluster image download progress details
        cluster_download_progress_info = dict()
        if result.get_child_by_name('progress-status'):
            cluster_download_progress_info['progress_status'] = result.get_child_content('progress-status')
            cluster_download_progress_info['progress_details'] = result.get_child_content('progress-details')
            cluster_download_progress_info['failure_reason'] = result.get_child_content('failure-reason')
            return cluster_download_progress_info
        return None

    def autosupport_log(self):
        """
        Autosupport log for software_update
        :return:
        """
        results = netapp_utils.get_cserver(self.server)
        cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
        netapp_utils.ems_log_event("na_ontap_software_update", cserver)

    def apply(self):
        """
        Apply action to update ONTAP software
        """
        if self.parameters.get('https') is not True:
            self.module.fail_json(msg='https parameter must be True')
        changed = False
        self.autosupport_log()
        current = self.cluster_image_get()
        validation_reports = 'only available after update'
        if self.module.check_mode:
            pass
        else:
            if self.parameters.get('nodes'):
                for node in self.parameters['nodes']:
                    self.cluster_image_get_for_node(node)
            if self.parameters.get('state') == 'present' and current:
                package_exists = self.cluster_image_package_download()
                if package_exists is False:
                    cluster_download_progress = self.cluster_image_package_download_progress()
                    while cluster_download_progress.get('progress_status') == 'async_pkg_get_phase_running':
                        time.sleep(5)
                        cluster_download_progress = self.cluster_image_package_download_progress()
                    if cluster_download_progress.get('progress_status') == 'async_pkg_get_phase_complete':
                        changed = True
                    else:
                        self.module.fail_json(msg='Error downloading package: %s'
                                                  % (cluster_download_progress['failure_reason']))
                if self.parameters['download_only'] is False:
                    self.cluster_image_update()
                    changed = True
                    # delete package once update is completed
                    cluster_update_progress = dict()
                    time_left = self.parameters['timeout']
                    polling_interval = 25
                    # assume in_progress if dict is empty
                    while time_left > 0 and cluster_update_progress.get('overall_status', 'in_progress') == 'in_progress':
                        time.sleep(polling_interval)
                        time_left -= polling_interval
                        cluster_update_progress = self.cluster_image_update_progress_get(ignore_connection_error=True)
                    if cluster_update_progress.get('overall_status') == 'completed':
                        validation_reports = str(cluster_update_progress.get('validation_reports'))
                        self.cluster_image_package_delete()
                    else:
                        cluster_update_progress = self.cluster_image_update_progress_get(ignore_connection_error=False)
                        if cluster_update_progress.get('overall_status') != 'completed':
                            if cluster_update_progress.get('overall_status') == 'in_progress':
                                msg = 'Timeout error'
                                action = '  Should the timeout value be increased?  Current value is %d seconds.' % self.parameters['timeout']
                                action += '  The software update continues in background.'
                            else:
                                msg = 'Error'
                                action = ''
                            msg += ' updating image: overall_status: %s.' % (cluster_update_progress.get('overall_status', 'cannot get status'))
                            msg += action
                            validation_reports = str(cluster_update_progress.get('validation_reports'))
                            self.module.fail_json(msg=msg, validation_reports=validation_reports)

        self.module.exit_json(changed=changed, validation_reports=validation_reports)