예제 #1
0
class NetAppOntapSnapshot(object):
    """
    Creates, modifies, and deletes a Snapshot
    """
    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'),
                from_name=dict(required=False, type='str'),
                snapshot=dict(required=True, type="str"),
                volume=dict(required=True, type="str"),
                async_bool=dict(required=False, type="bool", default=False),
                comment=dict(required=False, type="str"),
                snapmirror_label=dict(required=False, type="str"),
                ignore_owners=dict(required=False, type="bool", default=False),
                snapshot_instance_uuid=dict(required=False, type="str"),
                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)

        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'])
        return

    def get_snapshot(self, snapshot_name=None):
        """
        Checks to see if a snapshot exists or not
        :return: Return True if a snapshot exists, False if it doesn't
        """
        if snapshot_name is None:
            snapshot_name = self.parameters['snapshot']
        snapshot_obj = netapp_utils.zapi.NaElement("snapshot-get-iter")
        desired_attr = netapp_utils.zapi.NaElement("desired-attributes")
        snapshot_info = netapp_utils.zapi.NaElement('snapshot-info')
        comment = netapp_utils.zapi.NaElement('comment')
        snapmirror_label = netapp_utils.zapi.NaElement('snapmirror-label')
        # add more desired attributes that are allowed to be modified
        snapshot_info.add_child_elem(comment)
        snapshot_info.add_child_elem(snapmirror_label)
        desired_attr.add_child_elem(snapshot_info)
        snapshot_obj.add_child_elem(desired_attr)
        # compose query
        query = netapp_utils.zapi.NaElement("query")
        snapshot_info_obj = netapp_utils.zapi.NaElement("snapshot-info")
        snapshot_info_obj.add_new_child("name", snapshot_name)
        snapshot_info_obj.add_new_child("volume", self.parameters['volume'])
        snapshot_info_obj.add_new_child("vserver", self.parameters['vserver'])
        query.add_child_elem(snapshot_info_obj)
        snapshot_obj.add_child_elem(query)
        result = self.server.invoke_successfully(snapshot_obj, True)
        return_value = 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')
            snap_info = attributes_list.get_child_by_name('snapshot-info')
            return_value = {'comment': snap_info.get_child_content('comment')}
            if snap_info.get_child_by_name('snapmirror-label'):
                return_value['snapmirror_label'] = snap_info.get_child_content(
                    'snapmirror-label')
            else:
                return_value['snapmirror_label'] = None
        return return_value

    def create_snapshot(self):
        """
        Creates a new snapshot
        """
        snapshot_obj = netapp_utils.zapi.NaElement("snapshot-create")

        # set up required variables to create a snapshot
        snapshot_obj.add_new_child("snapshot", self.parameters['snapshot'])
        snapshot_obj.add_new_child("volume", self.parameters['volume'])
        # Set up optional variables to create a snapshot
        if self.parameters.get('async_bool'):
            snapshot_obj.add_new_child("async",
                                       str(self.parameters['async_bool']))
        if self.parameters.get('comment'):
            snapshot_obj.add_new_child("comment", self.parameters['comment'])
        if self.parameters.get('snapmirror_label'):
            snapshot_obj.add_new_child("snapmirror-label",
                                       self.parameters['snapmirror_label'])
        try:
            self.server.invoke_successfully(snapshot_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error creating snapshot %s: %s' %
                (self.parameters['snapshot'], to_native(error)),
                exception=traceback.format_exc())

    def delete_snapshot(self):
        """
        Deletes an existing snapshot
        """
        snapshot_obj = netapp_utils.zapi.NaElement("snapshot-delete")

        # Set up required variables to delete a snapshot
        snapshot_obj.add_new_child("snapshot", self.parameters['snapshot'])
        snapshot_obj.add_new_child("volume", self.parameters['volume'])
        # set up optional variables to delete a snapshot
        if self.parameters.get('ignore_owners'):
            snapshot_obj.add_new_child("ignore-owners",
                                       str(self.parameters['ignore_owners']))
        if self.parameters.get('snapshot_instance_uuid'):
            snapshot_obj.add_new_child(
                "snapshot-instance-uuid",
                self.parameters['snapshot_instance_uuid'])
        try:
            self.server.invoke_successfully(snapshot_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error deleting snapshot %s: %s' %
                (self.parameters['snapshot'], to_native(error)),
                exception=traceback.format_exc())

    def modify_snapshot(self):
        """
        Modify an existing snapshot
        :return:
        """
        snapshot_obj = netapp_utils.zapi.NaElement("snapshot-modify-iter")
        # Create query object, this is the existing object
        query = netapp_utils.zapi.NaElement("query")
        snapshot_info_obj = netapp_utils.zapi.NaElement("snapshot-info")
        snapshot_info_obj.add_new_child("name", self.parameters['snapshot'])
        snapshot_info_obj.add_new_child("vserver", self.parameters['vserver'])
        query.add_child_elem(snapshot_info_obj)
        snapshot_obj.add_child_elem(query)

        # this is what we want to modify in the snapshot object
        attributes = netapp_utils.zapi.NaElement("attributes")
        snapshot_info_obj = netapp_utils.zapi.NaElement("snapshot-info")
        snapshot_info_obj.add_new_child("name", self.parameters['snapshot'])
        if self.parameters.get('comment'):
            snapshot_info_obj.add_new_child("comment",
                                            self.parameters['comment'])
        if self.parameters.get('snapmirror_label'):
            snapshot_info_obj.add_new_child(
                "snapmirror-label", self.parameters['snapmirror_label'])
        attributes.add_child_elem(snapshot_info_obj)
        snapshot_obj.add_child_elem(attributes)
        try:
            self.server.invoke_successfully(snapshot_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error modifying snapshot %s: %s' %
                (self.parameters['snapshot'], to_native(error)),
                exception=traceback.format_exc())

    def rename_snapshot(self):
        """
        Rename the snapshot
        """
        snapshot_obj = netapp_utils.zapi.NaElement("snapshot-rename")

        # set up required variables to rename a snapshot
        snapshot_obj.add_new_child("current-name",
                                   self.parameters['from_name'])
        snapshot_obj.add_new_child("new-name", self.parameters['snapshot'])
        snapshot_obj.add_new_child("volume", self.parameters['volume'])
        try:
            self.server.invoke_successfully(snapshot_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error renaming snapshot %s to %s: %s' %
                (self.parameters['from_name'], self.parameters['snapshot'],
                 to_native(error)),
                exception=traceback.format_exc())

    def apply(self):
        """
        Check to see which play we should run
        """
        current = self.get_snapshot()
        netapp_utils.ems_log_event("na_ontap_snapshot", self.server)
        rename, cd_action = None, None
        modify = {}
        if self.parameters.get('from_name'):
            current_old_name = self.get_snapshot(self.parameters['from_name'])
            rename = self.na_helper.is_rename_action(current_old_name, current)
            modify = self.na_helper.get_modified_attributes(
                current_old_name, self.parameters)
        else:
            cd_action = self.na_helper.get_cd_action(current, self.parameters)
            if 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 rename:
                    self.rename_snapshot()
                if cd_action == 'create':
                    self.create_snapshot()
                elif cd_action == 'delete':
                    self.delete_snapshot()
                elif modify:
                    self.modify_snapshot()
        self.module.exit_json(changed=self.na_helper.changed)
예제 #2
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',
                              default=None,
                              choices=[
                                  'any', 'nfs', 'nfs3', 'nfs4', 'cifs',
                                  'flexcache'
                              ]),
                client_match=dict(required=False, type='list'),
                ro_rule=dict(required=False,
                             type='list',
                             default=None,
                             choices=[
                                 'any', 'none', 'never', 'krb5', 'krb5i',
                                 'krb5p', 'ntlm', 'sys'
                             ]),
                rw_rule=dict(required=False,
                             type='list',
                             default=None,
                             choices=[
                                 'any', 'none', 'never', 'krb5', 'krb5i',
                                 'krb5p', 'ntlm', 'sys'
                             ]),
                super_user_security=dict(required=False,
                                         type='list',
                                         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'),
                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'}

    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 not self.get_export_policy():
                    self.create_export_policy()
                if action == 'create':
                    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)
예제 #3
0
class NetAppONTAPasup(object):
    """Class with autosupport methods"""
    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'),
                node_name=dict(required=True, type='str'),
                transport=dict(required=False,
                               type='str',
                               choices=['smtp', 'http', 'https']),
                noteto=dict(required=False, type='list'),
                post_url=dict(required=False, type='str'),
                support=dict(required=False, type='bool'),
                mail_hosts=dict(required=False, type='list'),
                from_address=dict(required=False, type='str'),
                partner_addresses=dict(required=False, type='list'),
                to_addresses=dict(required=False, type='list'),
                proxy_url=dict(required=False, type='str'),
                hostname_in_subject=dict(required=False, type='bool'),
            ))

        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)
        # present or absent requires modifying state to enabled or disabled
        self.parameters['service_state'] = 'started' if self.parameters[
            'state'] == 'present' else 'stopped'
        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)

    def set_playbook_zapi_key_map(self):
        self.na_helper.zapi_string_keys = {
            'node_name': 'node-name',
            'transport': 'transport',
            'post_url': 'post-url',
            'from_address': 'from',
            'proxy_url': 'proxy-url'
        }
        self.na_helper.zapi_list_keys = {
            'noteto': ('noteto', 'mail-address'),
            'mail_hosts': ('mail-hosts', 'string'),
            'partner_addresses': ('partner-address', 'mail-address'),
            'to_addresses': ('to', 'mail-address'),
        }
        self.na_helper.zapi_bool_keys = {
            'support': 'is-support-enabled',
            'hostname_in_subject': 'is-node-in-subject'
        }

    def get_autosupport_config(self):
        """
        Invoke zapi - get current autosupport details
        :return: dict()
        """
        asup_details = netapp_utils.zapi.NaElement('autosupport-config-get')
        asup_details.add_new_child('node-name', self.parameters['node_name'])
        asup_info = dict()
        try:
            result = self.server.invoke_successfully(asup_details,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='%s' % to_native(error),
                                  exception=traceback.format_exc())
        # zapi invoke successful
        asup_attr_info = result.get_child_by_name(
            'attributes').get_child_by_name('autosupport-config-info')
        asup_info['service_state'] = 'started' if asup_attr_info[
            'is-enabled'] == 'true' else 'stopped'
        for item_key, zapi_key in self.na_helper.zapi_string_keys.items():
            asup_info[item_key] = asup_attr_info[zapi_key]
        for item_key, zapi_key in self.na_helper.zapi_bool_keys.items():
            asup_info[item_key] = self.na_helper.get_value_for_bool(
                from_zapi=True, value=asup_attr_info[zapi_key])
        for item_key, zapi_key in self.na_helper.zapi_list_keys.items():
            parent, dummy = zapi_key
            asup_info[item_key] = self.na_helper.get_value_for_list(
                from_zapi=True,
                zapi_parent=asup_attr_info.get_child_by_name(parent))
        return asup_info

    def modify_autosupport_config(self, modify):
        """
        Invoke zapi - modify autosupport config
        @return: NaElement object / FAILURE with an error_message
        """
        asup_details = {'node-name': self.parameters['node_name']}
        if modify.get('service_state'):
            asup_details['is-enabled'] = 'true' if modify.get(
                'service_state') == 'started' else 'false'
        asup_config = netapp_utils.zapi.NaElement('autosupport-config-modify')
        for item_key in modify:
            if item_key in self.na_helper.zapi_string_keys:
                zapi_key = self.na_helper.zapi_string_keys.get(item_key)
                asup_details[zapi_key] = modify[item_key]
            elif item_key in self.na_helper.zapi_bool_keys:
                zapi_key = self.na_helper.zapi_bool_keys.get(item_key)
                asup_details[zapi_key] = self.na_helper.get_value_for_bool(
                    from_zapi=False, value=modify[item_key])
            elif item_key in self.na_helper.zapi_list_keys:
                parent_key, child_key = self.na_helper.zapi_list_keys.get(
                    item_key)
                asup_config.add_child_elem(
                    self.na_helper.get_value_for_list(
                        from_zapi=False,
                        zapi_parent=parent_key,
                        zapi_child=child_key,
                        data=modify.get(item_key)))
        asup_config.translate_struct(asup_details)
        try:
            return self.server.invoke_successfully(asup_config,
                                                   enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='%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_autosupport", cserver)

    def apply(self):
        """
        Apply action to autosupport
        """
        current = self.get_autosupport_config()
        modify = self.na_helper.get_modified_attributes(
            current, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                self.modify_autosupport_config(modify)
        self.module.exit_json(changed=self.na_helper.changed)
예제 #4
0
class NetAppOntapNetPort(object):
    """
        Modify a Net port
    """
    def __init__(self):
        """
            Initialize the Ontap Net Port Class
        """
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(
                state=dict(required=False,
                           choices=['present'],
                           default='present'),
                node=dict(required=True, type="str"),
                ports=dict(required=True, type="list", aliases=['port']),
                mtu=dict(required=False, type="str", default=None),
                autonegotiate_admin=dict(required=False,
                                         type="str",
                                         default=None),
                duplex_admin=dict(required=False, type="str", default=None),
                speed_admin=dict(required=False, type="str", default=None),
                flowcontrol_admin=dict(required=False,
                                       type="str",
                                       default=None),
                ipspace=dict(required=False, type="str", default=None),
            ))

        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)
        return

    def set_playbook_zapi_key_map(self):
        self.na_helper.zapi_string_keys = {
            'mtu': 'mtu',
            'autonegotiate_admin': 'is-administrative-auto-negotiate',
            'duplex_admin': 'administrative-duplex',
            'speed_admin': 'administrative-speed',
            'flowcontrol_admin': 'administrative-flowcontrol',
            'ipspace': 'ipspace'
        }

    def get_net_port(self, port):
        """
        Return details about the net port
        :param: port: Name of the port
        :return: Dictionary with current state of the port. None if not found.
        :rtype: dict
        """
        net_port_get = netapp_utils.zapi.NaElement('net-port-get-iter')
        attributes = {
            'query': {
                'net-port-info': {
                    'node': self.parameters['node'],
                    'port': port
                }
            }
        }
        net_port_get.translate_struct(attributes)

        try:
            result = self.server.invoke_successfully(net_port_get, True)
            if result.get_child_by_name('num-records') and int(
                    result.get_child_content('num-records')) >= 1:
                port_info = result['attributes-list']['net-port-info']
                port_details = dict()
            else:
                return None
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error getting net ports for %s: %s' %
                                  (self.parameters['node'], to_native(error)),
                                  exception=traceback.format_exc())

        for item_key, zapi_key in self.na_helper.zapi_string_keys.items():
            port_details[item_key] = port_info.get_child_content(zapi_key)
        return port_details

    def modify_net_port(self, port, modify):
        """
        Modify a port

        :param port: Name of the port
        :param modify: dict with attributes to be modified
        :return: None
        """
        port_modify = netapp_utils.zapi.NaElement('net-port-modify')
        port_attributes = {'node': self.parameters['node'], 'port': port}
        for key in modify:
            if key in self.na_helper.zapi_string_keys:
                zapi_key = self.na_helper.zapi_string_keys.get(key)
                port_attributes[zapi_key] = modify[key]
        port_modify.translate_struct(port_attributes)
        try:
            self.server.invoke_successfully(port_modify, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error modifying net ports for %s: %s' %
                                  (self.parameters['node'], to_native(error)),
                                  exception=traceback.format_exc())

    def autosupport_log(self):
        """
        AutoSupport log for na_ontap_net_port
        :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("na_ontap_net_port", cserver)

    def apply(self):
        """
        Run Module based on play book
        """

        self.autosupport_log()
        # Run the task for all ports in the list of 'ports'
        for port in self.parameters['ports']:
            current = self.get_net_port(port)
            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.modify_net_port(port, modify)
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapSubnet(object):
    """
    Create, Modifies and Destroys a subnet
    """
    def __init__(self):
        """
        Initialize the ONTAP Subnet class
        """
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 name=dict(required=True, type='str'),
                 from_name=dict(required=False, type='str'),
                 broadcast_domain=dict(required=False, type='str'),
                 gateway=dict(required=False, type='str'),
                 ip_ranges=dict(required=False, type=list),
                 ipspace=dict(required=False, type='str'),
                 subnet=dict(required=False, 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)

        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)
        return

    def get_subnet(self, name=None):
        """
        Return details about the subnet
        :param:
            name : Name of the subnet
        :return: Details about the subnet. None if not found.
        :rtype: dict
        """
        if name is None:
            name = self.parameters.get('name')

        subnet_iter = netapp_utils.zapi.NaElement('net-subnet-get-iter')
        subnet_info = netapp_utils.zapi.NaElement('net-subnet-info')
        subnet_info.add_new_child('subnet-name', name)

        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(subnet_info)

        subnet_iter.add_child_elem(query)

        result = self.server.invoke_successfully(subnet_iter, True)

        return_value = None
        # check if query returns the expected subnet
        if result.get_child_by_name('num-records') and \
                int(result.get_child_content('num-records')) == 1:

            subnet_attributes = result.get_child_by_name(
                'attributes-list').get_child_by_name('net-subnet-info')
            broadcast_domain = subnet_attributes.get_child_content(
                'broadcast-domain')
            gateway = subnet_attributes.get_child_content('gateway')
            ipspace = subnet_attributes.get_child_content('ipspace')
            subnet = subnet_attributes.get_child_content('subnet')
            name = subnet_attributes.get_child_content('subnet-name')

            ip_ranges = []
            range_obj = subnet_attributes.get_child_by_name(
                'ip-ranges').get_children()
            for elem in range_obj:
                ip_ranges.append(elem.get_content())

            return_value = {
                'name': name,
                'broadcast_domain': broadcast_domain,
                'gateway': gateway,
                'ip_ranges': ip_ranges,
                'ipspace': ipspace,
                'subnet': subnet
            }

        return return_value

    def create_subnet(self):
        """
        Creates a new subnet
        """
        options = {
            'subnet-name': self.parameters.get('name'),
            'broadcast-domain': self.parameters.get('broadcast_domain'),
            'subnet': self.parameters.get('subnet')
        }
        subnet_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'net-subnet-create', **options)

        if self.parameters.get('gateway'):
            subnet_create.add_new_child('gateway',
                                        self.parameters.get('gateway'))
        if self.parameters.get('ip_ranges'):
            subnet_ips = netapp_utils.zapi.NaElement('ip-ranges')
            subnet_create.add_child_elem(subnet_ips)
            for ip_range in self.parameters.get('ip_ranges'):
                subnet_ips.add_new_child('ip-range', ip_range)
        if self.parameters.get('ipspace'):
            subnet_create.add_new_child('ipspace',
                                        self.parameters.get('ipspace'))

        try:
            self.server.invoke_successfully(subnet_create, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error creating subnet %s: %s' %
                (self.parameters.get('name'), to_native(error)),
                exception=traceback.format_exc())

    def delete_subnet(self):
        """
        Deletes a subnet
        """
        subnet_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'net-subnet-destroy',
            **{'subnet-name': self.parameters.get('name')})

        try:
            self.server.invoke_successfully(subnet_delete, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error deleting subnet %s: %s' %
                (self.parameters.get('name'), to_native(error)),
                exception=traceback.format_exc())

    def modify_subnet(self):
        """
        Modifies a subnet
        """
        options = {'subnet-name': self.parameters.get('name')}

        subnet_modify = netapp_utils.zapi.NaElement.create_node_with_children(
            'net-subnet-modify', **options)

        if self.parameters.get('gateway'):
            subnet_modify.add_new_child('gateway',
                                        self.parameters.get('gateway'))
        if self.parameters.get('ip_ranges'):
            subnet_ips = netapp_utils.zapi.NaElement('ip-ranges')
            subnet_modify.add_child_elem(subnet_ips)
            for ip_range in self.parameters.get('ip_ranges'):
                subnet_ips.add_new_child('ip-range', ip_range)
        if self.parameters.get('ipspace'):
            subnet_modify.add_new_child('ipspace',
                                        self.parameters.get('ipspace'))
        if self.parameters.get('subnet'):
            subnet_modify.add_new_child('subnet',
                                        self.parameters.get('subnet'))

        try:
            self.server.invoke_successfully(subnet_modify, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error modifying subnet %s: %s' %
                (self.parameters.get('name'), to_native(error)),
                exception=traceback.format_exc())

    def rename_subnet(self):
        """
        TODO
        """
        options = {
            'subnet-name': self.parameters.get('from_name'),
            'new-name': self.parameters.get('name')
        }

        subnet_rename = netapp_utils.zapi.NaElement.create_node_with_children(
            'net-subnet-rename', **options)

        if self.parameters.get('ipspace'):
            subnet_rename.add_new_child('ipspace',
                                        self.parameters.get('ipspace'))

        try:
            self.server.invoke_successfully(subnet_rename, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error renaming subnet %s: %s' %
                (self.parameters.get('name'), to_native(error)),
                exception=traceback.format_exc())

    def apply(self):
        '''Apply action to subnet'''
        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_net_subnet", cserver)
        current = self.get_subnet()
        cd_action, rename = None, None

        if self.parameters.get('from_name'):
            rename = self.na_helper.is_rename_action(
                self.get_subnet(self.parameters.get('from_name')), current)
            if rename is False:
                self.module.fail_json(
                    msg="Error renaming: subnet %s does not exist" %
                    self.parameters.get('from_name'))
        else:
            cd_action = self.na_helper.get_cd_action(current, self.parameters)

        modify = self.na_helper.get_modified_attributes(
            current, self.parameters)
        for attribute in modify:
            if attribute in ['broadcast_domain']:
                self.module.fail_json(
                    msg=
                    'Error modifying subnet %s: cannot modify broadcast_domain parameter.'
                    % self.parameters.get('name'))

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if rename:
                    self.rename_subnet()
                # If rename is True, cd_action is NOne but modify could be true
                if cd_action == 'create':
                    for attribute in ['subnet', 'broadcast_domain']:
                        if not self.parameters.get(attribute):
                            self.module.fail_json(
                                msg='Error - missing required arguments: %s.' %
                                attribute)
                    self.create_subnet()
                elif cd_action == 'delete':
                    self.delete_subnet()
                elif modify:
                    self.modify_subnet()
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppONTAPQuotas(object):
    '''Class with quotas methods'''
    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'),
                 vserver=dict(required=True, type='str'),
                 volume=dict(required=True, type='str'),
                 quota_target=dict(required=True, type='str'),
                 qtree=dict(required=False, type='str', default=""),
                 type=dict(required=True,
                           type='str',
                           choices=['user', 'group', 'tree']),
                 policy=dict(required=False, type='str'),
                 set_quota_status=dict(required=False, type='bool'),
                 file_limit=dict(required=False, type='str', default='-'),
                 disk_limit=dict(required=False, type='str', default='-'),
                 threshold=dict(required=False, type='str', default='-')))

        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)

        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 get_quota_status(self):
        """
        Return details about the quota status
        :param:
            name : volume name
        :return: status of the quota. None if not found.
        :rtype: dict
        """
        quota_status_get = netapp_utils.zapi.NaElement('quota-status')
        quota_status_get.translate_struct(
            {'volume': self.parameters['volume']})
        try:
            result = self.server.invoke_successfully(quota_status_get,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error fetching quotas status info: %s' %
                                  to_native(error),
                                  exception=traceback.format_exc())
        if result:
            return result['status']
        return None

    def get_quotas(self):
        """
        Get quota details
        :return: name of volume if quota exists, None otherwise
        """
        quota_get = netapp_utils.zapi.NaElement('quota-list-entries-iter')
        query = {
            'query': {
                'quota-entry': {
                    'volume': self.parameters['volume'],
                    'quota-target': self.parameters['quota_target'],
                    'quota-type': self.parameters['type'],
                    'vserver': self.parameters['vserver']
                }
            }
        }
        quota_get.translate_struct(query)
        if self.parameters.get('policy'):
            quota_get['query']['quota-entry'].add_new_child(
                'policy', self.parameters['policy'])
        try:
            result = self.server.invoke_successfully(quota_get,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error fetching quotas info: %s' %
                                  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_values = {
                'volume':
                result['attributes-list']['quota-entry']['volume'],
                'file_limit':
                result['attributes-list']['quota-entry']['file-limit'],
                'disk_limit':
                result['attributes-list']['quota-entry']['disk-limit'],
                'threshold':
                result['attributes-list']['quota-entry']['threshold']
            }
            return return_values
        return None

    def quota_entry_set(self):
        """
        Adds a quota entry
        """
        options = {
            'volume': self.parameters['volume'],
            'quota-target': self.parameters['quota_target'],
            'quota-type': self.parameters['type'],
            'qtree': self.parameters['qtree'],
            'file-limit': self.parameters['file_limit'],
            'disk-limit': self.parameters['disk_limit'],
            'threshold': self.parameters['threshold']
        }
        if self.parameters.get('policy'):
            options['policy'] = self.parameters['policy']
        set_entry = netapp_utils.zapi.NaElement.create_node_with_children(
            'quota-set-entry', **options)
        try:
            self.server.invoke_successfully(set_entry, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error adding/modifying quota entry %s: %s' %
                (self.parameters['volume'], to_native(error)),
                exception=traceback.format_exc())

    def quota_entry_delete(self):
        """
        Deletes a quota entry
        """
        options = {
            'volume': self.parameters['volume'],
            'quota-target': self.parameters['quota_target'],
            'quota-type': self.parameters['type'],
            'qtree': self.parameters['qtree']
        }
        set_entry = netapp_utils.zapi.NaElement.create_node_with_children(
            'quota-delete-entry', **options)
        if self.parameters.get('policy'):
            set_entry.add_new_child('policy', self.parameters['policy'])
        try:
            self.server.invoke_successfully(set_entry, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error deleting quota entry %s: %s' %
                (self.parameters['volume'], to_native(error)),
                exception=traceback.format_exc())

    def quota_entry_modify(self, modify_attrs):
        """
        Modifies a quota entry
        """
        options = {
            'volume': self.parameters['volume'],
            'quota-target': self.parameters['quota_target'],
            'quota-type': self.parameters['type'],
            'qtree': self.parameters['qtree']
        }
        options.update(modify_attrs)
        if self.parameters.get('policy'):
            options['policy'] = str(self.parameters['policy'])
        modify_entry = netapp_utils.zapi.NaElement.create_node_with_children(
            'quota-modify-entry', **options)
        try:
            self.server.invoke_successfully(modify_entry,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error modifying quota entry %s: %s' %
                (self.parameters['volume'], to_native(error)),
                exception=traceback.format_exc())

    def on_or_off_quota(self, status):
        """
        on or off quota
        """
        quota = netapp_utils.zapi.NaElement.create_node_with_children(
            status, **{'volume': self.parameters['volume']})
        try:
            self.server.invoke_successfully(quota, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error setting %s for %s: %s' %
                (status, self.parameters['volume'], to_native(error)),
                exception=traceback.format_exc())

    def apply(self):
        """
        Apply action to quotas
        """
        netapp_utils.ems_log_event("na_ontap_quotas", self.server)
        modify_quota_status = None
        modify_quota = None
        current = self.get_quotas()
        if 'set_quota_status' in self.parameters:
            quota_status = self.get_quota_status()
            if quota_status is not None:
                quota_status_action = self.na_helper.get_modified_attributes(
                    {
                        'set_quota_status':
                        True if quota_status == 'on' else False
                    }, self.parameters)
                if quota_status_action:
                    modify_quota_status = 'quota-on' if quota_status_action[
                        'set_quota_status'] else 'quota-off'
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if cd_action is None:
            modify_quota = 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.quota_entry_set()
                elif cd_action == 'delete':
                    self.quota_entry_delete()
                elif modify_quota is not None:
                    for key in list(modify_quota):
                        modify_quota[key.replace("_",
                                                 "-")] = modify_quota.pop(key)
                    self.quota_entry_modify(modify_quota)
                if modify_quota_status is not None:
                    self.on_or_off_quota(modify_quota_status)
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppONTAPSnapmirror(object):
    """
    Class with Snapmirror 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'),
                 source_vserver=dict(required=False, type='str'),
                 destination_vserver=dict(required=False, type='str'),
                 source_volume=dict(required=False, type='str'),
                 destination_volume=dict(required=False, type='str'),
                 source_path=dict(required=False, type='str'),
                 destination_path=dict(required=False, type='str'),
                 schedule=dict(required=False, type='str'),
                 policy=dict(required=False, type='str'),
                 relationship_type=dict(required=False,
                                        type='str',
                                        choices=[
                                            'data_protection', 'load_sharing',
                                            'vault', 'restore',
                                            'transition_data_protection',
                                            'extended_data_protection'
                                        ]),
                 source_hostname=dict(required=False, type='str'),
                 connection_type=dict(required=False,
                                      type='str',
                                      choices=[
                                          'ontap_ontap', 'elementsw_ontap',
                                          'ontap_elementsw'
                                      ],
                                      default='ontap_ontap'),
                 source_username=dict(required=False, type='str'),
                 source_password=dict(required=False, type='str', no_log=True),
                 max_transfer_rate=dict(required=False, type='int'),
                 identity_preserve=dict(required=False, type='bool')))

        self.module = AnsibleModule(
            argument_spec=self.argument_spec,
            required_together=(['source_volume', 'destination_volume'],
                               ['source_vserver', 'destination_vserver']),
            supports_check_mode=True)

        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)
        # setup later if required
        self.source_server = None
        # only for ElementSW -> ONTAP snapmirroring, validate if ElementSW SDK is available
        if self.parameters.get('connection_type') in [
                'elementsw_ontap', 'ontap_elementsw'
        ]:
            if HAS_SF_SDK is False:
                self.module.fail_json(
                    msg="Unable to import the SolidFire Python SDK")
        if HAS_NETAPP_LIB is False:
            self.module.fail_json(
                msg="the python NetApp-Lib module is required")
        if self.parameters.get('connection_type') != 'ontap_elementsw':
            self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
        else:
            if self.parameters.get('source_username'):
                self.module.params['username'] = self.parameters[
                    'source_username']
            if self.parameters.get('source_password'):
                self.module.params['password'] = self.parameters[
                    'source_password']
            self.module.params['hostname'] = self.parameters['source_hostname']
            self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)

    def set_element_connection(self, kind):
        if kind == 'source':
            self.module.params['hostname'] = self.parameters['source_hostname']
            self.module.params['username'] = self.parameters['source_username']
            self.module.params['password'] = self.parameters['source_password']
        elif kind == 'destination':
            self.module.params['hostname'] = self.parameters['hostname']
            self.module.params['username'] = self.parameters['username']
            self.module.params['password'] = self.parameters['password']
        elem = netapp_utils.create_sf_connection(module=self.module)
        elementsw_helper = NaElementSWModule(elem)
        return elementsw_helper, elem

    def snapmirror_get_iter(self, destination=None):
        """
        Compose NaElement object to query current SnapMirror relations using destination-path
        SnapMirror relation for a destination path is unique
        :return: NaElement object for SnapMirror-get-iter
        """
        snapmirror_get_iter = netapp_utils.zapi.NaElement(
            'snapmirror-get-iter')
        query = netapp_utils.zapi.NaElement('query')
        snapmirror_info = netapp_utils.zapi.NaElement('snapmirror-info')
        if destination is None:
            destination = self.parameters['destination_path']
        snapmirror_info.add_new_child('destination-location', destination)
        query.add_child_elem(snapmirror_info)
        snapmirror_get_iter.add_child_elem(query)
        return snapmirror_get_iter

    def snapmirror_get(self, destination=None):
        """
        Get current SnapMirror relations
        :return: Dictionary of current SnapMirror details if query successful, else None
        """
        snapmirror_get_iter = self.snapmirror_get_iter(destination)
        snap_info = dict()
        try:
            result = self.server.invoke_successfully(snapmirror_get_iter,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error fetching snapmirror info: %s' %
                                  to_native(error),
                                  exception=traceback.format_exc())
        if result.get_child_by_name('num-records') and \
                int(result.get_child_content('num-records')) > 0:
            snapmirror_info = result.get_child_by_name(
                'attributes-list').get_child_by_name('snapmirror-info')
            snap_info['mirror_state'] = snapmirror_info.get_child_content(
                'mirror-state')
            snap_info['status'] = snapmirror_info.get_child_content(
                'relationship-status')
            snap_info['schedule'] = snapmirror_info.get_child_content(
                'schedule')
            snap_info['policy'] = snapmirror_info.get_child_content('policy')
            snap_info['relationship'] = snapmirror_info.get_child_content(
                'relationship-type')
            if snapmirror_info.get_child_by_name('max-transfer-rate'):
                snap_info['max_transfer_rate'] = int(
                    snapmirror_info.get_child_content('max-transfer-rate'))
            if snap_info['schedule'] is None:
                snap_info['schedule'] = ""
            return snap_info
        return None

    def check_if_remote_volume_exists(self):
        """
        Validate existence of source volume
        :return: True if volume exists, False otherwise
        """
        self.set_source_cluster_connection()
        # do a get volume to check if volume exists or not
        volume_info = netapp_utils.zapi.NaElement('volume-get-iter')
        volume_attributes = netapp_utils.zapi.NaElement('volume-attributes')
        volume_id_attributes = netapp_utils.zapi.NaElement(
            'volume-id-attributes')
        volume_id_attributes.add_new_child('name',
                                           self.parameters['source_volume'])
        # if source_volume is present, then source_vserver is also guaranteed to be present
        volume_id_attributes.add_new_child('vserver-name',
                                           self.parameters['source_vserver'])
        volume_attributes.add_child_elem(volume_id_attributes)
        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(volume_attributes)
        volume_info.add_child_elem(query)
        try:
            result = self.source_server.invoke_successfully(volume_info, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error fetching source volume details %s : %s' %
                (self.parameters['source_volume'], to_native(error)),
                exception=traceback.format_exc())
        if result.get_child_by_name('num-records') and int(
                result.get_child_content('num-records')) > 0:
            return True
        return False

    def snapmirror_create(self):
        """
        Create a SnapMirror relationship
        """
        if self.parameters.get('source_hostname') and self.parameters.get(
                'source_volume'):
            if not self.check_if_remote_volume_exists():
                self.module.fail_json(
                    msg=
                    'Source volume does not exist. Please specify a volume that exists'
                )
        options = {
            'source-location': self.parameters['source_path'],
            'destination-location': self.parameters['destination_path']
        }
        snapmirror_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'snapmirror-create', **options)
        if self.parameters.get('relationship_type'):
            snapmirror_create.add_new_child(
                'relationship-type', self.parameters['relationship_type'])
        if self.parameters.get('schedule'):
            snapmirror_create.add_new_child('schedule',
                                            self.parameters['schedule'])
        if self.parameters.get('policy'):
            snapmirror_create.add_new_child('policy',
                                            self.parameters['policy'])
        if self.parameters.get('max_transfer_rate'):
            snapmirror_create.add_new_child(
                'max-transfer-rate', str(self.parameters['max_transfer_rate']))
        if self.parameters.get('identity_preserve'):
            snapmirror_create.add_new_child(
                'identity-preserve', str(self.parameters['identity_preserve']))
        try:
            self.server.invoke_successfully(snapmirror_create,
                                            enable_tunneling=True)
            self.snapmirror_initialize()
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error creating SnapMirror %s' %
                                  to_native(error),
                                  exception=traceback.format_exc())

    def set_source_cluster_connection(self):
        """
        Setup ontap ZAPI server connection for source hostname
        :return: None
        """
        if self.parameters.get('source_username'):
            self.module.params['username'] = self.parameters['source_username']
        if self.parameters.get('source_password'):
            self.module.params['password'] = self.parameters['source_password']
        self.module.params['hostname'] = self.parameters['source_hostname']
        self.source_server = netapp_utils.setup_na_ontap_zapi(
            module=self.module)

    def delete_snapmirror(self, is_hci, relationship_type):
        """
        Delete a SnapMirror relationship
        #1. Quiesce the SnapMirror relationship at destination
        #2. Break the SnapMirror relationship at the destination
        #3. Release the SnapMirror at source
        #4. Delete SnapMirror at destination
        """
        if not is_hci:
            if not self.parameters.get('source_hostname'):
                self.module.fail_json(
                    msg='Missing parameters for delete: Please specify the '
                    'source cluster hostname to release the SnapMirror relation'
                )
        # Quiesce at destination
        self.snapmirror_quiesce()
        # Break at destination
        if relationship_type not in ['load_sharing', 'vault']:
            self.snapmirror_break()
        # if source is ONTAP, release the destination at source cluster
        if not is_hci:
            self.set_source_cluster_connection()
            if self.get_destination():
                # Release at source
                self.snapmirror_release()
        # Delete at destination
        self.snapmirror_delete()

    def snapmirror_quiesce(self):
        """
        Quiesce SnapMirror relationship - disable all future transfers to this destination
        """
        options = {'destination-location': self.parameters['destination_path']}

        snapmirror_quiesce = netapp_utils.zapi.NaElement.create_node_with_children(
            'snapmirror-quiesce', **options)
        try:
            self.server.invoke_successfully(snapmirror_quiesce,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error Quiescing SnapMirror : %s' %
                                  (to_native(error)),
                                  exception=traceback.format_exc())

    def snapmirror_delete(self):
        """
        Delete SnapMirror relationship at destination cluster
        """
        options = {'destination-location': self.parameters['destination_path']}

        snapmirror_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'snapmirror-destroy', **options)
        try:
            self.server.invoke_successfully(snapmirror_delete,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error deleting SnapMirror : %s' %
                                  (to_native(error)),
                                  exception=traceback.format_exc())

    def snapmirror_break(self, destination=None):
        """
        Break SnapMirror relationship at destination cluster
        """
        if destination is None:
            destination = self.parameters['destination_path']
        options = {'destination-location': destination}
        snapmirror_break = netapp_utils.zapi.NaElement.create_node_with_children(
            'snapmirror-break', **options)
        try:
            self.server.invoke_successfully(snapmirror_break,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error breaking SnapMirror relationship : %s' %
                (to_native(error)),
                exception=traceback.format_exc())

    def snapmirror_release(self):
        """
        Release SnapMirror relationship from source cluster
        """
        options = {'destination-location': self.parameters['destination_path']}
        snapmirror_release = netapp_utils.zapi.NaElement.create_node_with_children(
            'snapmirror-release', **options)
        try:
            self.source_server.invoke_successfully(snapmirror_release,
                                                   enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error releasing SnapMirror relationship : %s' %
                (to_native(error)),
                exception=traceback.format_exc())

    def snapmirror_abort(self):
        """
        Abort a SnapMirror relationship in progress
        """
        options = {'destination-location': self.parameters['destination_path']}
        snapmirror_abort = netapp_utils.zapi.NaElement.create_node_with_children(
            'snapmirror-abort', **options)
        try:
            self.server.invoke_successfully(snapmirror_abort,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error aborting SnapMirror relationship : %s' %
                (to_native(error)),
                exception=traceback.format_exc())

    def snapmirror_initialize(self):
        """
        Initialize SnapMirror based on relationship type
        """
        current = self.snapmirror_get()
        if current['mirror_state'] != 'snapmirrored':
            initialize_zapi = 'snapmirror-initialize'
            if self.parameters.get('relationship_type') and self.parameters[
                    'relationship_type'] == 'load_sharing':
                initialize_zapi = 'snapmirror-initialize-ls-set'
                options = {'source-location': self.parameters['source_path']}
            else:
                options = {
                    'destination-location': self.parameters['destination_path']
                }
            snapmirror_init = netapp_utils.zapi.NaElement.create_node_with_children(
                initialize_zapi, **options)
            try:
                self.server.invoke_successfully(snapmirror_init,
                                                enable_tunneling=True)
            except netapp_utils.zapi.NaApiError as error:
                self.module.fail_json(
                    msg='Error initializing SnapMirror : %s' %
                    (to_native(error)),
                    exception=traceback.format_exc())

    def snapmirror_modify(self, modify):
        """
        Modify SnapMirror schedule or policy
        """
        options = {'destination-location': self.parameters['destination_path']}
        snapmirror_modify = netapp_utils.zapi.NaElement.create_node_with_children(
            'snapmirror-modify', **options)
        if modify.get('schedule') is not None:
            snapmirror_modify.add_new_child('schedule', modify.get('schedule'))
        if modify.get('policy'):
            snapmirror_modify.add_new_child('policy', modify.get('policy'))
        if modify.get('max_transfer_rate'):
            snapmirror_modify.add_new_child(
                'max-transfer-rate', str(modify.get('max_transfer_rate')))
        try:
            self.server.invoke_successfully(snapmirror_modify,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error modifying SnapMirror schedule or policy : %s' %
                (to_native(error)),
                exception=traceback.format_exc())

    def snapmirror_update(self):
        """
        Update data in destination endpoint
        """
        options = {'destination-location': self.parameters['destination_path']}
        snapmirror_update = netapp_utils.zapi.NaElement.create_node_with_children(
            'snapmirror-update', **options)
        try:
            result = self.server.invoke_successfully(snapmirror_update,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error updating SnapMirror : %s' %
                                  (to_native(error)),
                                  exception=traceback.format_exc())

    def check_parameters(self):
        """
        Validate parameters and fail if one or more required params are missing
        Update source and destination path from vserver and volume parameters
        """
        if self.parameters['state'] == 'present'\
                and (self.parameters.get('source_path') or self.parameters.get('destination_path')):
            if not self.parameters.get(
                    'destination_path') or not self.parameters.get(
                        'source_path'):
                self.module.fail_json(
                    msg='Missing parameters: Source path or Destination path')
        elif self.parameters.get('source_volume'):
            if not self.parameters.get(
                    'source_vserver') or not self.parameters.get(
                        'destination_vserver'):
                self.module.fail_json(
                    msg=
                    'Missing parameters: source vserver or destination vserver or both'
                )
            self.parameters['source_path'] = self.parameters[
                'source_vserver'] + ":" + self.parameters['source_volume']
            self.parameters['destination_path'] = self.parameters['destination_vserver'] + ":" +\
                self.parameters['destination_volume']
        elif self.parameters.get('source_vserver'):
            self.parameters[
                'source_path'] = self.parameters['source_vserver'] + ":"
            self.parameters['destination_path'] = self.parameters[
                'destination_vserver'] + ":"

    def get_destination(self):
        result = None
        release_get = netapp_utils.zapi.NaElement(
            'snapmirror-get-destination-iter')
        query = netapp_utils.zapi.NaElement('query')
        snapmirror_dest_info = netapp_utils.zapi.NaElement(
            'snapmirror-destination-info')
        snapmirror_dest_info.add_new_child('destination-location',
                                           self.parameters['destination_path'])
        query.add_child_elem(snapmirror_dest_info)
        release_get.add_child_elem(query)
        try:
            result = self.source_server.invoke_successfully(
                release_get, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error fetching snapmirror destinations info: %s' %
                to_native(error),
                exception=traceback.format_exc())
        if result.get_child_by_name('num-records') and \
                int(result.get_child_content('num-records')) > 0:
            return True
        return None

    @staticmethod
    def element_source_path_format_matches(value):
        return re.match(
            pattern=r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\/lun\/[0-9]+",
            string=value)

    def check_elementsw_parameters(self, kind='source'):
        """
        Validate all ElementSW cluster parameters required for managing the SnapMirror relationship
        Validate if both source and destination paths are present
        Validate if source_path follows the required format
        Validate SVIP
        Validate if ElementSW volume exists
        :return: None
        """
        path = None
        if kind == 'destination':
            path = self.parameters.get('destination_path')
        elif kind == 'source':
            path = self.parameters.get('source_path')
        if path is None:
            self.module.fail_json(
                msg="Error: Missing required parameter %s_path for "
                "connection_type %s" %
                (kind, self.parameters['connection_type']))
        else:
            if NetAppONTAPSnapmirror.element_source_path_format_matches(
                    path) is None:
                self.module.fail_json(
                    msg="Error: invalid %s_path %s. "
                    "If the path is a ElementSW cluster, the value should be of the format"
                    " <Element_SVIP>:/lun/<Element_VOLUME_ID>" % (kind, path))
        # validate source_path
        elementsw_helper, elem = self.set_element_connection(kind)
        self.validate_elementsw_svip(path, elem)
        self.check_if_elementsw_volume_exists(path, elementsw_helper)

    def validate_elementsw_svip(self, path, elem):
        """
        Validate ElementSW cluster SVIP
        :return: None
        """
        result = None
        try:
            result = elem.get_cluster_info()
        except solidfire.common.ApiServerError as err:
            self.module.fail_json(msg="Error fetching SVIP",
                                  exception=to_native(err))
        if result and result.cluster_info.svip:
            cluster_svip = result.cluster_info.svip
            svip = path.split(':')[0]  # split IP address from source_path
            if svip != cluster_svip:
                self.module.fail_json(msg="Error: Invalid SVIP")

    def check_if_elementsw_volume_exists(self, path, elementsw_helper):
        """
        Check if remote ElementSW volume exists
        :return: None
        """
        volume_id, vol_id = None, path.split('/')[-1]
        try:
            volume_id = elementsw_helper.volume_id_exists(int(vol_id))
        except solidfire.common.ApiServerError as err:
            self.module.fail_json(msg="Error fetching Volume details",
                                  exception=to_native(err))

        if volume_id is None:
            self.module.fail_json(
                msg=
                "Error: Source volume does not exist in the ElementSW cluster")

    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)

    def apply(self):
        """
        Apply action to SnapMirror
        """
        self.asup_log_for_cserver("na_ontap_snapmirror")
        # source is ElementSW
        if self.parameters['state'] == 'present' and self.parameters.get(
                'connection_type') == 'elementsw_ontap':
            self.check_elementsw_parameters()
        elif self.parameters.get('connection_type') == 'ontap_elementsw':
            self.check_elementsw_parameters('destination')
        else:
            self.check_parameters()
        if self.parameters['state'] == 'present' and self.parameters.get(
                'connection_type') == 'ontap_elementsw':
            current_elementsw_ontap = self.snapmirror_get(
                self.parameters['source_path'])
            if current_elementsw_ontap is None:
                self.module.fail_json(
                    msg=
                    'Error: creating an ONTAP to ElementSW snapmirror relationship requires an '
                    'established SnapMirror relation from ElementSW to ONTAP cluster'
                )
        current = self.snapmirror_get()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        modify = self.na_helper.get_modified_attributes(
            current, self.parameters)
        element_snapmirror = False
        if cd_action == 'create':
            self.snapmirror_create()
        elif cd_action == 'delete':
            if current['status'] == 'transferring':
                self.snapmirror_abort()
            else:
                if self.parameters.get('connection_type') == 'elementsw_ontap':
                    element_snapmirror = True
                self.delete_snapmirror(element_snapmirror,
                                       current['relationship'])
        else:
            if modify:
                self.snapmirror_modify(modify)
            # check for initialize
            if current and current['mirror_state'] != 'snapmirrored':
                self.snapmirror_initialize()
                # set changed explicitly for initialize
                self.na_helper.changed = True
            # Update when create is called again, or modify is being called
            if self.parameters['state'] == 'present':
                self.snapmirror_update()
        self.module.exit_json(changed=self.na_helper.changed)
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,
                            choices=['present'],
                            default='present'),
                 address_type=dict(required=True, choices=['ipv4', 'ipv6']),
                 is_enabled=dict(required=True, type='bool'),
                 node=dict(required=True, type='str'),
                 dhcp=dict(required=False, 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 NetAppOntapLDAP(object):
    '''
    LDAP Client definition class
    '''
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(name=dict(required=True, type='str'),
                 skip_config_validation=dict(required=False,
                                             default=None,
                                             choices=['true', 'false']),
                 state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 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)

        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 get_ldap(self, client_config_name=None):
        '''
        Checks if LDAP config exists.

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

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

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

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

        result = self.server.invoke_successfully(config_info,
                                                 enable_tunneling=True)

        # Get LDAP configuration details
        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')
            config_info = attributes_list.get_child_by_name('ldap-config')

            # Define config details structure
            config_details = {
                'client_config':
                config_info.get_child_content('client-config'),
                'skip_config_validation':
                config_info.get_child_content('skip-config-validation'),
                'vserver':
                config_info.get_child_content('vserver')
            }

        return config_details

    def create_ldap(self):
        '''
        Create LDAP configuration
        '''
        options = {
            'client-config': self.parameters['name'],
            'client-enabled': 'true'
        }

        if self.parameters.get('skip_config_validation') is not None:
            options['skip-config-validation'] = self.parameters[
                'skip_config_validation']

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

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

    def delete_ldap(self):
        '''
        Delete LDAP configuration
        '''
        ldap_client_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'ldap-config-delete', **{})

        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 configuration %s: %s' %
                (self.parameters['name'], to_native(errcatch)),
                exception=traceback.format_exc())

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

        for attribute in modify:
            if attribute == 'skip_config_validation':
                ldap_modify.add_new_child('skip-config-validation',
                                          self.parameters[attribute])

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

    def apply(self):
        '''Call create/modify/delete operations.'''
        current = self.get_ldap()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        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", self.server)

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_ldap()
                elif cd_action == 'delete':
                    self.delete_ldap()
                elif modify:
                    self.modify_ldap(modify)
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppONTAPJob(object):
    '''Class with job schedule cron methods'''
    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'),
                 name=dict(required=True, type='str'),
                 job_minutes=dict(required=False, type='list'),
                 job_months=dict(required=False, type='list'),
                 job_hours=dict(required=False, type='list'),
                 job_days_of_month=dict(required=False, type='list'),
                 job_days_of_week=dict(required=False, type='list')))

        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)

    def set_playbook_zapi_key_map(self):
        self.na_helper.zapi_string_keys = {
            'name': 'job-schedule-name',
        }
        self.na_helper.zapi_list_keys = {
            'job_minutes': ('job-schedule-cron-minute', 'cron-minute'),
            'job_months': ('job-schedule-cron-month', 'cron-month'),
            'job_hours': ('job-schedule-cron-hour', 'cron-hour'),
            'job_days_of_month':
            ('job-schedule-cron-day', 'cron-day-of-month'),
            'job_days_of_week':
            ('job-schedule-cron-day-of-week', 'cron-day-of-week')
        }

    def get_job_schedule(self):
        """
        Return details about the job
        :param:
            name : Job name
        :return: Details about the Job. None if not found.
        :rtype: dict
        """
        job_get_iter = netapp_utils.zapi.NaElement(
            'job-schedule-cron-get-iter')
        job_get_iter.translate_struct({
            'query': {
                'job-schedule-cron-info': {
                    'job-schedule-name': self.parameters['name']
                }
            }
        })
        result = self.server.invoke_successfully(job_get_iter, True)
        job_details = None
        # check if job exists
        if result.get_child_by_name('num-records') and int(
                result['num-records']) >= 1:
            job_info = result['attributes-list']['job-schedule-cron-info']
            job_details = dict()
            for item_key, zapi_key in self.na_helper.zapi_string_keys.items():
                job_details[item_key] = job_info[zapi_key]
            for item_key, zapi_key in self.na_helper.zapi_list_keys.items():
                parent, dummy = zapi_key
                job_details[item_key] = self.na_helper.get_value_for_list(
                    from_zapi=True,
                    zapi_parent=job_info.get_child_by_name(parent))
                # if any of the job_hours, job_minutes, job_months, job_days are empty:
                # it means the value is -1 for ZAPI
                if not job_details[item_key]:
                    job_details[item_key] = ['-1']
        return job_details

    def add_job_details(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 item_key in values:
            if item_key in self.na_helper.zapi_string_keys:
                zapi_key = self.na_helper.zapi_string_keys.get(item_key)
                na_element_object[zapi_key] = values[item_key]
            elif item_key in self.na_helper.zapi_list_keys:
                parent_key, child_key = self.na_helper.zapi_list_keys.get(
                    item_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.get(item_key)))

    def create_job_schedule(self):
        """
        Creates a job schedule
        """
        # job_minutes is mandatory for create
        if self.parameters.get('job_minutes') is None:
            self.module.fail_json(
                msg='Error: missing required parameter job_minutes for create')

        job_schedule_create = netapp_utils.zapi.NaElement(
            'job-schedule-cron-create')
        self.add_job_details(job_schedule_create, self.parameters)
        try:
            self.server.invoke_successfully(job_schedule_create,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error creating job schedule %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def delete_job_schedule(self):
        """
        Delete a job schedule
        """
        job_schedule_delete = netapp_utils.zapi.NaElement(
            'job-schedule-cron-destroy')
        self.add_job_details(job_schedule_delete, self.parameters)
        try:
            self.server.invoke_successfully(job_schedule_delete,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error deleting job schedule %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def modify_job_schedule(self, params):
        """
        modify a job schedule
        """
        job_schedule_modify = netapp_utils.zapi.NaElement.create_node_with_children(
            'job-schedule-cron-modify',
            **{'job-schedule-name': self.parameters['name']})
        self.add_job_details(job_schedule_modify, params)
        try:
            self.server.invoke_successfully(job_schedule_modify,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error modifying job schedule %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def autosupport_log(self):
        """
        Autosupport log for job_schedule
        :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("na_ontap_job_schedule", cserver)

    def apply(self):
        """
        Apply action to job-schedule
        """
        self.autosupport_log()
        current = self.get_job_schedule()
        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:
                if action == 'create':
                    self.create_job_schedule()
                elif action == 'delete':
                    self.delete_job_schedule()
                elif modify:
                    self.modify_job_schedule(modify)
        self.module.exit_json(changed=self.na_helper.changed)
예제 #11
0
class NetAppOntapUser(object):
    """
    Common operations to manage users and roles.
    """

    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'),
            name=dict(required=True, type='str'),

            applications=dict(required=True, type='list', aliases=['application'],
                              choices=['console', 'http', 'ontapi', 'rsh', 'snmp',
                                       'sp', 'service-processor', 'ssh', 'telnet'],),
            authentication_method=dict(required=True, type='str',
                                       choices=['community', 'password', 'publickey', 'domain', 'nsswitch', 'usm', 'cert']),
            set_password=dict(required=False, type='str', no_log=True),
            role_name=dict(required=False, type='str'),
            lock_user=dict(required=False, type='bool'),
            vserver=dict(required=True, type='str'),
        ))

        self.module = AnsibleModule(
            argument_spec=self.argument_spec,
            required_if=[
                ('state', 'present', ['role_name'])
            ],
            supports_check_mode=True
        )

        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'])

    def get_user(self, application=None):
        """
        Checks if the user exists.
        :param: application: application to grant access to
        :return:
            Dictionary if user found
            None if user is not found
        """
        security_login_get_iter = netapp_utils.zapi.NaElement('security-login-get-iter')
        query_details = netapp_utils.zapi.NaElement.create_node_with_children(
            'security-login-account-info', **{'vserver': self.parameters['vserver'],
                                              'user-name': self.parameters['name'],
                                              'authentication-method': self.parameters['authentication_method']})
        if application is not None:
            query_details.add_new_child('application', application)
        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(query_details)
        security_login_get_iter.add_child_elem(query)
        try:
            result = self.server.invoke_successfully(security_login_get_iter,
                                                     enable_tunneling=False)
            if result.get_child_by_name('num-records') and \
                    int(result.get_child_content('num-records')) >= 1:
                interface_attributes = result.get_child_by_name('attributes-list').\
                    get_child_by_name('security-login-account-info')
                return_value = {
                    'lock_user': interface_attributes.get_child_content('is-locked'),
                    'role_name': interface_attributes.get_child_content('role-name')
                }
                return return_value
            return None
        except netapp_utils.zapi.NaApiError as error:
            # Error 16034 denotes a user not being found.
            if to_native(error.code) == "16034":
                return None
            # Error 16043 denotes the user existing, but the application missing
            elif to_native(error.code) == "16043":
                return None
            else:
                self.module.fail_json(msg='Error getting user %s: %s' % (self.parameters['name'], to_native(error)),
                                      exception=traceback.format_exc())

    def create_user(self, application):
        """
        creates the user for the given application and authentication_method
        :param: application: application to grant access to
        """
        user_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'security-login-create', **{'vserver': self.parameters['vserver'],
                                        'user-name': self.parameters['name'],
                                        'application': application,
                                        'authentication-method': self.parameters['authentication_method'],
                                        'role-name': self.parameters.get('role_name')})
        if self.parameters.get('set_password') is not None:
            user_create.add_new_child('password', self.parameters.get('set_password'))

        try:
            self.server.invoke_successfully(user_create,
                                            enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error creating user %s: %s' % (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def lock_given_user(self):
        """
        locks the user

        :return:
            True if user locked
            False if lock user is not performed
        :rtype: bool
        """
        user_lock = netapp_utils.zapi.NaElement.create_node_with_children(
            'security-login-lock', **{'vserver': self.parameters['vserver'],
                                      'user-name': self.parameters['name']})

        try:
            self.server.invoke_successfully(user_lock,
                                            enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error locking user %s: %s' % (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def unlock_given_user(self):
        """
        unlocks the user

        :return:
            True if user unlocked
            False if unlock user is not performed
        :rtype: bool
        """
        user_unlock = netapp_utils.zapi.NaElement.create_node_with_children(
            'security-login-unlock', **{'vserver': self.parameters['vserver'],
                                        'user-name': self.parameters['name']})

        try:
            self.server.invoke_successfully(user_unlock,
                                            enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as error:
            if to_native(error.code) == '13114':
                return False
            else:
                self.module.fail_json(msg='Error unlocking user %s: %s' % (self.parameters['name'], to_native(error)),
                                      exception=traceback.format_exc())
        return True

    def delete_user(self, application):
        """
        deletes the user for the given application and authentication_method
        :param: application: application to grant access to
        """
        user_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'security-login-delete', **{'vserver': self.parameters['vserver'],
                                        'user-name': self.parameters['name'],
                                        'application': application,
                                        'authentication-method': self.parameters['authentication_method']})

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

    def change_password(self):
        """
        Changes the password

        :return:
            True if password updated
            False if password is not updated
        :rtype: bool
        """
        # self.server.set_vserver(self.parameters['vserver'])
        modify_password = netapp_utils.zapi.NaElement.create_node_with_children(
            'security-login-modify-password', **{
                'new-password': str(self.parameters.get('set_password')),
                'user-name': self.parameters['name']})
        try:
            self.server.invoke_successfully(modify_password,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            if to_native(error.code) == '13114':
                return False
            # if the user give the same password, instead of returning an error, return ok
            if to_native(error.code) == '13214' and \
                    (error.message.startswith('New password must be different than last 6 passwords.')
                     or error.message.startswith('New password must be different than the old password.')):
                return False
            self.module.fail_json(msg='Error setting password for user %s: %s' % (self.parameters['name'], to_native(error)),
                                      exception=traceback.format_exc())

        self.server.set_vserver(None)
        return True

    def modify_user(self, application):
        """
        Modify user
        """
        user_modify = netapp_utils.zapi.NaElement.create_node_with_children(
            'security-login-modify', **{'vserver': self.parameters['vserver'],
                                        'user-name': self.parameters['name'],
                                        'application': application,
                                        'authentication-method': self.parameters['authentication_method'],
                                        'role-name': self.parameters.get('role_name')})

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

    def apply(self):
        create_delete_decision = {}
        modify_decision = {}

        netapp_utils.ems_log_event("na_ontap_user", self.server)
        for application in self.parameters['applications']:
            current = self.get_user(application)
            if current is not None:
                current['lock_user'] = self.na_helper.get_value_for_bool(True, current['lock_user'])

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

            if cd_action is not None:
                create_delete_decision[application] = cd_action
            else:
                modify_decision[application] = self.na_helper.get_modified_attributes(current, self.parameters)

        if not create_delete_decision and self.parameters.get('state') == 'present':
            if self.parameters.get('set_password') is not None:
                self.na_helper.changed = True

        if self.na_helper.changed:

            if self.module.check_mode:
                pass
            else:
                for application in create_delete_decision:
                    if create_delete_decision[application] == 'create':
                        self.create_user(application)
                    elif create_delete_decision[application] == 'delete':
                        self.delete_user(application)
                lock_user = False
                for application in modify_decision:
                    if 'role_name' in modify_decision[application]:
                        self.modify_user(application)
                    if 'lock_user' in modify_decision[application]:
                        lock_user = True

                if lock_user:
                    if self.parameters.get('lock_user'):
                        self.lock_given_user()
                    else:
                        self.unlock_given_user()
                if not create_delete_decision and self.parameters.get('set_password') is not None:
                    # if change password return false nothing has changed so we need to set changed to False
                    self.na_helper.changed = self.change_password()
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppONTAPMotd(object):
    def __init__(self):
        argument_spec = netapp_utils.na_ontap_host_argument_spec()
        argument_spec.update(
            dict(state=dict(required=False,
                            default='present',
                            choices=['present', 'absent']),
                 vserver=dict(required=True, type='str'),
                 message=dict(default='', type='str'),
                 show_cluster_motd=dict(default=True, type='bool')))

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

        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")

        self.server = netapp_utils.setup_na_ontap_zapi(
            module=self.module, vserver=self.parameters['vserver'])

    def motd_get_iter(self):
        """
        Compose NaElement object to query current motd
        :return: NaElement object for vserver-motd-get-iter
        """
        motd_get_iter = netapp_utils.zapi.NaElement('vserver-motd-get-iter')
        query = netapp_utils.zapi.NaElement('query')
        motd_info = netapp_utils.zapi.NaElement('vserver-motd-info')
        motd_info.add_new_child('is-cluster-message-enabled',
                                str(self.parameters['show_cluster_motd']))
        motd_info.add_new_child('vserver', self.parameters['vserver'])
        query.add_child_elem(motd_info)
        motd_get_iter.add_child_elem(query)
        return motd_get_iter

    def motd_get(self):
        """
        Get current motd
        :return: Dictionary of current motd details if query successful, else None
        """
        motd_get_iter = self.motd_get_iter()
        motd_result = dict()
        try:
            result = self.server.invoke_successfully(motd_get_iter,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error fetching motd info: %s' %
                                  to_native(error),
                                  exception=traceback.format_exc())
        if result.get_child_by_name('num-records') and \
                int(result.get_child_content('num-records')) > 0:
            motd_info = result.get_child_by_name(
                'attributes-list').get_child_by_name('vserver-motd-info')
            motd_result['message'] = motd_info.get_child_content('message')
            motd_result['message'] = str(motd_result['message']).rstrip()
            motd_result[
                'show_cluster_motd'] = True if motd_info.get_child_content(
                    'is-cluster-message-enabled') == 'true' else False
            motd_result['vserver'] = motd_info.get_child_content('vserver')
            return motd_result
        return None

    def modify_motd(self):
        motd_create = netapp_utils.zapi.NaElement('vserver-motd-modify-iter')
        motd_create.add_new_child('message', self.parameters['message'])
        motd_create.add_new_child(
            'is-cluster-message-enabled', 'true'
            if self.parameters['show_cluster_motd'] is True else 'false')
        query = netapp_utils.zapi.NaElement('query')
        motd_info = netapp_utils.zapi.NaElement('vserver-motd-info')
        motd_info.add_new_child('vserver', self.parameters['vserver'])
        query.add_child_elem(motd_info)
        motd_create.add_child_elem(query)
        try:
            self.server.invoke_successfully(motd_create,
                                            enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as err:
            self.module.fail_json(msg="Error creating motd: %s" %
                                  (to_native(err)),
                                  exception=traceback.format_exc())
        return motd_create

    def apply(self):
        """
        Applies action from playbook
        """
        netapp_utils.ems_log_event("na_ontap_motd", self.server)
        current = self.motd_get()
        if self.parameters['state'] == 'present' and self.parameters[
                'message'] == "":
            self.module.fail_json(msg="message parameter cannot be empty")
        if self.parameters['state'] == 'absent':
            # Just make sure it is empty
            self.parameters['message'] = ''
            if current['message'] == 'None':
                current = None
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if cd_action is None and self.parameters['state'] == 'present':
            self.na_helper.get_modified_attributes(current, self.parameters)

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                self.modify_motd()
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapKerberosRealm(object):
    '''
    Kerberos Realm definition class
    '''
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(admin_server_ip=dict(required=False, default=None,
                                      type='str'),
                 admin_server_port=dict(required=False,
                                        default=None,
                                        type='str'),
                 clock_skew=dict(required=False, default=None, type='str'),
                 comment=dict(required=False, default=None, type='str'),
                 kdc_ip=dict(required_if=[["state", "present"]],
                             default=None,
                             type='str'),
                 kdc_port=dict(required=False, default=None, type='str'),
                 kdc_vendor=dict(required_if=[["state", "present"]],
                                 default=None,
                                 type='str',
                                 choices=['Microsoft', 'Other']),
                 pw_server_ip=dict(required=False, default=None, type='str'),
                 pw_server_port=dict(required=False, default=None, type='str'),
                 realm=dict(required=True, type='str'),
                 state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 vserver=dict(required=True, type='str')))

        self.module = AnsibleModule(
            argument_spec=self.argument_spec,
            supports_check_mode=True,
            required_if=[('state', 'present', ['kdc_vendor', 'kdc_ip'])],
        )
        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 = [
            'admin_server_ip',
            'admin_server_port',
            'clock_skew',
            'kdc_ip',
            'kdc_port',
            'kdc_vendor',
        ]

    def get_krbrealm(self, realm_name=None, vserver_name=None):
        '''
        Checks if Kerberos Realm config exists.

        :return:
            kerberos realm object if found
            None if not found
        :rtype: object/None
        '''
        # Make query
        krbrealm_info = netapp_utils.zapi.NaElement('kerberos-realm-get-iter')

        if realm_name is None:
            realm_name = self.parameters['realm']

        if vserver_name is None:
            vserver_name = self.parameters['vserver']

        query_details = netapp_utils.zapi.NaElement.create_node_with_children(
            'kerberos-realm', **{
                'realm': realm_name,
                'vserver-name': vserver_name
            })

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

        result = self.server.invoke_successfully(krbrealm_info,
                                                 enable_tunneling=True)

        # Get Kerberos Realm details
        krbrealm_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')
            config_info = attributes_list.get_child_by_name('kerberos-realm')

            krbrealm_details = {
                'admin_server_ip':
                config_info.get_child_content('admin-server-ip'),
                'admin_server_port':
                config_info.get_child_content('admin-server-port'),
                'clock_skew':
                config_info.get_child_content('clock-skew'),
                'kdc_ip':
                config_info.get_child_content('kdc-ip'),
                'kdc_port':
                config_info.get_child_content('kdc-port'),
                'kdc_vendor':
                config_info.get_child_content('kdc-vendor'),
                'pw_server_ip':
                config_info.get_child_content('password-server-ip'),
                'pw_server_port':
                config_info.get_child_content('password-server-port'),
                'realm':
                config_info.get_child_content('realm'),
                'vserver':
                config_info.get_child_content('vserver'),
            }

        return krbrealm_details

    def create_krbrealm(self):
        '''supported
        Create Kerberos Realm configuration
        '''
        options = {'realm': self.parameters['realm']}

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

        if self.parameters.get('pw_server_ip') is not None:
            options['password-server-ip'] = self.parameters['pw_server_ip']
        if self.parameters.get('pw_server_port') is not None:
            options['password-server-port'] = self.parameters['pw_server_port']

        # Initialize NaElement
        krbrealm_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'kerberos-realm-create', **options)

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

    def delete_krbrealm(self):
        '''
        Delete Kerberos Realm configuration
        '''
        krbrealm_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'kerberos-realm-delete', **{'realm': self.parameters['realm']})

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

    def modify_krbrealm(self, modify):
        '''
        Modify Kerberos Realm
        :param modify: list of modify attributes
        '''
        krbrealm_modify = netapp_utils.zapi.NaElement('kerberos-realm-modify')
        krbrealm_modify.add_new_child('realm', self.parameters['realm'])

        for attribute in modify:
            if attribute in self.simple_attributes:
                krbrealm_modify.add_new_child(
                    str(attribute).replace('_', '-'),
                    self.parameters[attribute])
            if attribute == 'pw_server_ip':
                krbrealm_modify.add_new_child('password-server-ip',
                                              self.parameters['pw_server_ip'])
            if attribute == 'pw_server_port':
                krbrealm_modify.add_new_child(
                    'password-server-port', self.parameters['pw_server_port'])

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

    def apply(self):
        '''Call create/modify/delete operations.'''
        current = self.get_krbrealm()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        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_kerberos_realm", self.server)

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_krbrealm()
                elif cd_action == 'delete':
                    self.delete_krbrealm()
                elif modify:
                    self.modify_krbrealm(modify)
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapBroadcastDomain(object):
    """
        Create, Modifies and Destroys a Broadcast domain
    """
    def __init__(self):
        """
            Initialize the ONTAP Broadcast Domain class
        """
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(
                state=dict(required=False,
                           choices=['present', 'absent'],
                           default='present'),
                name=dict(required=True,
                          type='str',
                          aliases=["broadcast_domain"]),
                ipspace=dict(required=False, type='str'),
                mtu=dict(required=False, type='str'),
                ports=dict(required=False, type='list'),
                from_name=dict(required=False, 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)

        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)
        return

    def get_broadcast_domain(self, broadcast_domain=None):
        """
        Return details about the broadcast domain
        :param broadcast_domain: specific broadcast domain to get.
        :return: Details about the broadcast domain. None if not found.
        :rtype: dict
        """
        if broadcast_domain is None:
            broadcast_domain = self.parameters['name']
        domain_get_iter = netapp_utils.zapi.NaElement(
            'net-port-broadcast-domain-get-iter')
        broadcast_domain_info = netapp_utils.zapi.NaElement(
            'net-port-broadcast-domain-info')
        broadcast_domain_info.add_new_child('broadcast-domain',
                                            broadcast_domain)
        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(broadcast_domain_info)
        domain_get_iter.add_child_elem(query)
        result = self.server.invoke_successfully(domain_get_iter, True)
        domain_exists = None
        # check if broadcast_domain exists
        if result.get_child_by_name('num-records') and \
                int(result.get_child_content('num-records')) == 1:
            domain_info = result.get_child_by_name('attributes-list').\
                get_child_by_name('net-port-broadcast-domain-info')
            domain_name = domain_info.get_child_content('broadcast-domain')
            domain_mtu = domain_info.get_child_content('mtu')
            domain_ipspace = domain_info.get_child_content('ipspace')
            domain_ports = domain_info.get_child_by_name('ports')
            if domain_ports is not None:
                ports = [
                    port.get_child_content('port')
                    for port in domain_ports.get_children()
                ]
            else:
                ports = []
            domain_exists = {
                'domain-name': domain_name,
                'mtu': domain_mtu,
                'ipspace': domain_ipspace,
                'ports': ports
            }
        return domain_exists

    def create_broadcast_domain(self):
        """
        Creates a new broadcast domain
        """
        domain_obj = netapp_utils.zapi.NaElement(
            'net-port-broadcast-domain-create')
        domain_obj.add_new_child("broadcast-domain", self.parameters['name'])
        if self.parameters.get('ipspace'):
            domain_obj.add_new_child("ipspace", self.parameters['ipspace'])
        if self.parameters.get('mtu'):
            domain_obj.add_new_child("mtu", self.parameters['mtu'])
        if self.parameters.get('ports'):
            ports_obj = netapp_utils.zapi.NaElement('ports')
            domain_obj.add_child_elem(ports_obj)
            for port in self.parameters['ports']:
                ports_obj.add_new_child('net-qualified-port-name', port)
        try:
            self.server.invoke_successfully(domain_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error creating broadcast domain %s: %s' %
                (self.parameters['name'], to_native(error)),
                exception=traceback.format_exc())

    def delete_broadcast_domain(self, broadcast_domain=None):
        """
        Deletes a broadcast domain
        """
        if broadcast_domain is None:
            broadcast_domain = self.parameters['name']
        domain_obj = netapp_utils.zapi.NaElement(
            'net-port-broadcast-domain-destroy')
        domain_obj.add_new_child("broadcast-domain", broadcast_domain)
        if self.parameters.get('ipspace'):
            domain_obj.add_new_child("ipspace", self.parameters['ipspace'])
        try:
            self.server.invoke_successfully(domain_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error deleting broadcast domain %s: %s' %
                (broadcast_domain, to_native(error)),
                exception=traceback.format_exc())

    def modify_broadcast_domain(self):
        """
        Modifies ipspace and mtu options of a broadcast domain
        """
        domain_obj = netapp_utils.zapi.NaElement(
            'net-port-broadcast-domain-modify')
        domain_obj.add_new_child("broadcast-domain", self.parameters['name'])
        if self.parameters.get('mtu'):
            domain_obj.add_new_child("mtu", self.parameters['mtu'])
        if self.parameters.get('ipspace'):
            domain_obj.add_new_child("ipspace", self.parameters['ipspace'])
        try:
            self.server.invoke_successfully(domain_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error modifying broadcast domain %s: %s' %
                (self.parameters['name'], to_native(error)),
                exception=traceback.format_exc())

    def split_broadcast_domain(self):
        """
        split broadcast domain
        """
        domain_obj = netapp_utils.zapi.NaElement(
            'net-port-broadcast-domain-split')
        domain_obj.add_new_child("broadcast-domain",
                                 self.parameters['from_name'])
        domain_obj.add_new_child("new-broadcast-domain",
                                 self.parameters['name'])
        if self.parameters.get('ports'):
            ports_obj = netapp_utils.zapi.NaElement('ports')
            domain_obj.add_child_elem(ports_obj)
            for port in self.parameters['ports']:
                ports_obj.add_new_child('net-qualified-port-name', port)
        if self.parameters.get('ipspace'):
            domain_obj.add_new_child("ipspace", self.parameters['ipspace'])
        try:
            self.server.invoke_successfully(domain_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error splitting broadcast domain %s: %s' %
                (self.parameters['name'], to_native(error)),
                exception=traceback.format_exc())
        if len(self.get_broadcast_domain_ports(
                self.parameters['from_name'])) == 0:
            self.delete_broadcast_domain(self.parameters['from_name'])

    def modify_redirect(self, modify):
        """
        :param modify: modify attributes.
        """
        for attribute in modify.keys():
            if attribute == 'mtu':
                self.modify_broadcast_domain()
            if attribute == 'ports':
                self.modify_broadcast_domain_ports()

    def get_modify_attributes(self, current, split):
        """
        :param current: current state.
        :param split: True or False of split action.
        :return: list of modified attributes.
        """
        modify = None
        if self.parameters['state'] == 'present':
            # split already handled ipspace and ports.
            if self.parameters.get('from_name'):
                current = self.get_broadcast_domain(
                    self.parameters['from_name'])
                if split:
                    modify = self.na_helper.get_modified_attributes(
                        current, self.parameters)
                    if modify.get('ipspace'):
                        del modify['ipspace']
                    if modify.get('ports'):
                        del modify['ports']
        # ipspace can not be modified.
            else:
                modify = self.na_helper.get_modified_attributes(
                    current, self.parameters)
                if modify.get('ipspace'):
                    self.module.fail_json(
                        msg=
                        'A domain ipspace can not be modified after the domain has been created.',
                        exception=traceback.format_exc())
        return modify

    def modify_broadcast_domain_ports(self):
        """
        compare current and desire ports. Call add or remove ports methods if needed.
        :return: None.
        """
        current_ports = self.get_broadcast_domain_ports()
        expect_ports = self.parameters['ports']
        # if want to remove all ports, simply delete the broadcast domain.
        if len(expect_ports) == 0:
            self.delete_broadcast_domain()
            return
        ports_to_remove = list(set(current_ports) - set(expect_ports))
        ports_to_add = list(set(expect_ports) - set(current_ports))

        if len(ports_to_add) > 0:
            self.add_broadcast_domain_ports(ports_to_add)

        if len(ports_to_remove) > 0:
            self.delete_broadcast_domain_ports(ports_to_remove)

    def add_broadcast_domain_ports(self, ports):
        """
        Creates new broadcast domain ports
        """
        domain_obj = netapp_utils.zapi.NaElement(
            'net-port-broadcast-domain-add-ports')
        domain_obj.add_new_child("broadcast-domain", self.parameters['name'])
        if self.parameters.get('ipspace'):
            domain_obj.add_new_child("ipspace", self.parameters['ipspace'])
        if ports:
            ports_obj = netapp_utils.zapi.NaElement('ports')
            domain_obj.add_child_elem(ports_obj)
            for port in ports:
                ports_obj.add_new_child('net-qualified-port-name', port)
        try:
            self.server.invoke_successfully(domain_obj, True)
            return True
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error creating port for broadcast domain %s: %s' %
                (self.parameters['name'], to_native(error)),
                exception=traceback.format_exc())

    def delete_broadcast_domain_ports(self, ports):
        """
        Deletes broadcast domain ports
        :param: ports to be deleted.
        """
        domain_obj = netapp_utils.zapi.NaElement(
            'net-port-broadcast-domain-remove-ports')
        domain_obj.add_new_child("broadcast-domain", self.parameters['name'])
        if self.parameters.get('ipspace'):
            domain_obj.add_new_child("ipspace", self.parameters['ipspace'])
        if ports:
            ports_obj = netapp_utils.zapi.NaElement('ports')
            domain_obj.add_child_elem(ports_obj)
            for port in ports:
                ports_obj.add_new_child('net-qualified-port-name', port)
        try:
            self.server.invoke_successfully(domain_obj, True)
            return True
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error deleting port for broadcast domain %s: %s' %
                (self.parameters['name'], to_native(error)),
                exception=traceback.format_exc())

    def get_broadcast_domain_ports(self, broadcast_domain=None):
        """
        Return details about the broadcast domain ports.
        :return: Details about the broadcast domain ports. None if not found.
        :rtype: list
        """
        if broadcast_domain is None:
            broadcast_domain = self.parameters['name']
        domain_get_iter = netapp_utils.zapi.NaElement(
            'net-port-broadcast-domain-get-iter')
        broadcast_domain_info = netapp_utils.zapi.NaElement(
            'net-port-broadcast-domain-info')
        broadcast_domain_info.add_new_child('broadcast-domain',
                                            broadcast_domain)
        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(broadcast_domain_info)
        domain_get_iter.add_child_elem(query)
        result = self.server.invoke_successfully(domain_get_iter, True)
        ports = []
        if result.get_child_by_name('num-records') and \
                int(result.get_child_content('num-records')) == 1:
            domain_info = result.get_child_by_name(
                'attributes-list').get_child_by_name(
                    'net-port-broadcast-domain-info')
            domain_ports = domain_info.get_child_by_name('ports')
            if domain_ports is not None:
                ports = [
                    port.get_child_content('port')
                    for port in domain_ports.get_children()
                ]
        return ports

    def apply(self):
        """
        Run Module based on play book
        """
        self.asup_log_for_cserver("na_ontap_broadcast_domain")
        current = self.get_broadcast_domain()
        cd_action, split = None, None
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if cd_action == 'create':
            # either create new domain or split domain.
            if self.parameters.get('from_name'):
                split = self.na_helper.is_rename_action(
                    self.get_broadcast_domain(self.parameters['from_name']),
                    current)
                if split is None:
                    self.module.fail_json(
                        msg='A domain can not be split if it does not exist.',
                        exception=traceback.format_exc())
                if split:
                    cd_action = None
        modify = self.get_modify_attributes(current, split)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if split:
                    self.split_broadcast_domain()
                if cd_action == 'create':
                    self.create_broadcast_domain()
                elif cd_action == 'delete':
                    self.delete_broadcast_domain()
                elif modify:
                    self.modify_redirect(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)
예제 #15
0
class NetAppOntapUnixUser(object):
    """
    Common operations to manage users and roles.
    """
    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'),
                name=dict(required=True, type='str'),
                group_id=dict(required=False, type='int'),
                id=dict(required=False, type='int'),
                full_name=dict(required=False, type='str'),
                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)

        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 get_unix_user(self):
        """
        Checks if the UNIX user exists.

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

        get_unix_user = netapp_utils.zapi.NaElement(
            'name-mapping-unix-user-get-iter')
        attributes = {
            'query': {
                'unix-user-info': {
                    'user-name': self.parameters['name'],
                    'vserver': self.parameters['vserver'],
                }
            }
        }
        get_unix_user.translate_struct(attributes)
        try:
            result = self.server.invoke_successfully(get_unix_user,
                                                     enable_tunneling=True)
            if result.get_child_by_name('num-records') and int(
                    result.get_child_content('num-records')) >= 1:
                user_info = result['attributes-list']['unix-user-info']
                return {
                    'group_id': int(user_info['group-id']),
                    'id': int(user_info['user-id']),
                    'full_name': user_info['full-name']
                }
            return None
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error getting UNIX user %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def create_unix_user(self):
        """
        Creates an UNIX user in the specified Vserver

        :return: None
        """
        if self.parameters.get('group_id') is None or self.parameters.get(
                'id') is None:
            self.module.fail_json(
                msg=
                'Error: Missing one or more required parameters for create: (group_id, id)'
            )

        user_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'name-mapping-unix-user-create', **{
                'user-name': self.parameters['name'],
                'group-id': str(self.parameters['group_id']),
                'user-id': str(self.parameters['id'])
            })
        if self.parameters.get('full_name') is not None:
            user_create.add_new_child('full-name',
                                      self.parameters['full_name'])

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

    def delete_unix_user(self):
        """
        Deletes an UNIX user from a vserver

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

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

    def modify_unix_user(self, params):
        user_modify = netapp_utils.zapi.NaElement.create_node_with_children(
            'name-mapping-unix-user-modify',
            **{'user-name': self.parameters['name']})
        for key in params:
            if key == 'group_id':
                user_modify.add_new_child('group-id', str(params['group_id']))
            if key == 'id':
                user_modify.add_new_child('user-id', str(params['id']))
            if key == 'full_name':
                user_modify.add_new_child('full-name', params['full_name'])

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

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

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

        :return: None
        """
        self.autosupport_log()
        current = self.get_unix_user()
        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_user()
                elif cd_action == 'delete':
                    self.delete_unix_user()
                else:
                    self.modify_unix_user(modify)
        self.module.exit_json(changed=self.na_helper.changed)
예제 #16
0
class NetAppONTAPNdmp(object):
    '''
    modify vserver cifs security
    '''
    def __init__(self):

        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.modifiable_options = dict(
            abort_on_disk_error=dict(required=False, type='bool'),
            authtype=dict(required=False, type='list'),
            backup_log_enable=dict(required=False, type='bool'),
            data_port_range=dict(required=False, type='str'),
            debug_enable=dict(required=False, type='bool'),
            debug_filter=dict(required=False, type='str'),
            dump_detailed_stats=dict(required=False, type='bool'),
            dump_logical_find=dict(required=False, type='str'),
            enable=dict(required=False, type='bool'),
            fh_dir_retry_interval=dict(required=False, type='int'),
            fh_node_retry_interval=dict(required=False, type='int'),
            ignore_ctime_enabled=dict(required=False, type='bool'),
            is_secure_control_connection_enabled=dict(required=False,
                                                      type='bool'),
            offset_map_enable=dict(required=False, type='bool'),
            per_qtree_exclude_enable=dict(required=False, type='bool'),
            preferred_interface_role=dict(required=False, type='list'),
            restore_vm_cache_size=dict(required=False, type='int'),
            secondary_debug_filter=dict(required=False, type='str'),
            tcpnodelay=dict(required=False, type='bool'),
            tcpwinsize=dict(required=False, type='int'))
        self.argument_spec.update(
            dict(vserver=dict(required=True, type='str')))

        self.argument_spec.update(self.modifiable_options)

        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)

        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 ndmp_get_iter(self):
        """
        get current vserver ndmp attributes.
        :return: a dict of ndmp attributes.
        """
        ndmp_get = netapp_utils.zapi.NaElement(
            'ndmp-vserver-attributes-get-iter')
        query = netapp_utils.zapi.NaElement('query')
        ndmp_info = netapp_utils.zapi.NaElement('ndmp-vserver-attributes-info')
        ndmp_info.add_new_child('vserver', self.parameters['vserver'])
        query.add_child_elem(ndmp_info)
        ndmp_get.add_child_elem(query)
        ndmp_details = dict()
        try:
            result = self.server.invoke_successfully(ndmp_get,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error fetching ndmp 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:
            ndmp_attributes = result.get_child_by_name(
                'attributes-list').get_child_by_name(
                    'ndmp-vserver-attributes-info')
            self.get_ndmp_details(ndmp_details, ndmp_attributes)
        return ndmp_details

    def get_ndmp_details(self, ndmp_details, ndmp_attributes):
        """
        :param ndmp_details: a dict of current ndmp.
        :param ndmp_attributes: ndmp returned from api call in xml format.
        :return: None
        """
        for option in self.modifiable_options.keys():
            option_type = self.modifiable_options[option]['type']
            if option_type == 'bool':
                ndmp_details[option] = self.str_to_bool(
                    ndmp_attributes.get_child_content(
                        self.attribute_to_name(option)))
            elif option_type == 'int':
                ndmp_details[option] = int(
                    ndmp_attributes.get_child_content(
                        self.attribute_to_name(option)))
            elif option_type == 'list':
                child_list = ndmp_attributes.get_child_by_name(
                    self.attribute_to_name(option))
                values = [
                    child.get_content() for child in child_list.get_children()
                ]
                ndmp_details[option] = values
            else:
                ndmp_details[option] = ndmp_attributes.get_child_content(
                    self.attribute_to_name(option))

    def modify_ndmp(self, modify):
        """
        :param modify: A list of attributes to modify
        :return: None
        """
        ndmp_modify = netapp_utils.zapi.NaElement(
            'ndmp-vserver-attributes-modify')
        for attribute in modify:
            if attribute == 'authtype':
                authtypes = netapp_utils.zapi.NaElement('authtype')
                types = self.parameters['authtype']
                for authtype in types:
                    authtypes.add_new_child('ndmpd-authtypes', authtype)
                ndmp_modify.add_child_elem(authtypes)
            elif attribute == 'preferred_interface_role':
                preferred_interface_roles = netapp_utils.zapi.NaElement(
                    'preferred-interface-role')
                roles = self.parameters['preferred_interface_role']
                for role in roles:
                    preferred_interface_roles.add_new_child(
                        'netport-role', role)
                ndmp_modify.add_child_elem(preferred_interface_roles)
            else:
                ndmp_modify.add_new_child(self.attribute_to_name(attribute),
                                          str(self.parameters[attribute]))
        try:
            self.server.invoke_successfully(ndmp_modify, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as e:
            self.module.fail_json(msg='Error modifying ndmp on %s: %s' %
                                  (self.parameters['vserver'], to_native(e)),
                                  exception=traceback.format_exc())

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

    @staticmethod
    def str_to_bool(s):
        if s == 'true':
            return True
        else:
            return False

    def apply(self):
        """Call modify operations."""
        self.asup_log_for_cserver("na_ontap_ndmp")
        current = self.ndmp_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.modify_ndmp(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)
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)

        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 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')
            cifs_security_details[
                'kerberos_clock_skew'] = cifs_security_info.get_child_content(
                    'kerberos-clock-skew')
            cifs_security_details[
                'kerberos_ticket_age'] = cifs_security_info.get_child_content(
                    'kerberos-ticket-age')
            cifs_security_details[
                'kerberos_renew_age'] = cifs_security_info.get_child_content(
                    'kerberos-renew-age')
            cifs_security_details[
                'kerberos_kdc_timeout'] = cifs_security_info.get_child_content(
                    'kerberos-kdc-timeout')
            cifs_security_details['is_signing_required'] = bool(
                cifs_security_info.get_child_content('is-signing-required'))
            cifs_security_details['is_password_complexity_required'] = bool(
                cifs_security_info.get_child_content(
                    'is-password-complexity-required'))
            cifs_security_details['is_aes_encryption_enabled'] = bool(
                cifs_security_info.get_child_content(
                    'is-aes-encryption-enabled'))
            cifs_security_details['is_smb_encryption_required'] = bool(
                cifs_security_info.get_child_content(
                    'is-smb-encryption-required'))
            cifs_security_details[
                'lm_compatibility_level'] = cifs_security_info.get_child_content(
                    'lm-compatibility-level')
            cifs_security_details['referral_enabled_for_ad_ldap'] = bool(
                cifs_security_info.get_child_content(
                    'referral-enabled-for-ad-ldap'))
            cifs_security_details[
                'session_security_for_ad_ldap'] = cifs_security_info.get_child_content(
                    'session-security-for-ad-ldap')
            cifs_security_details[
                'smb1_enabled_for_dc_connections'] = cifs_security_info.get_child_content(
                    'smb1-enabled-for-dc-connections')
            cifs_security_details[
                'smb2_enabled_for_dc_connections'] = cifs_security_info.get_child_content(
                    'smb2-enabled-for-dc-connections')
            cifs_security_details['use_start_tls_for_ad_ldap'] = bool(
                cifs_security_info.get_child_content(
                    'use-start-tls-for-ad-ldap'))
            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)
class NetAppOntapSnapshotPolicy(object):
    """
    Creates and deletes a Snapshot 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'),
                name=dict(required=True, type="str"),
                enabled=dict(required=False, type="bool"),
                # count is a list of integers
                count=dict(required=False, type="list", elements="int"),
                comment=dict(required=False, type="str"),
                schedule=dict(required=False, type="list", elements="str"),
                snapmirror_label=dict(required=False,
                                      type="list",
                                      elements="str"),
                vserver=dict(required=False, type="str")))
        self.module = AnsibleModule(argument_spec=self.argument_spec,
                                    required_if=[
                                        ('state', 'present',
                                         ['enabled', 'count', 'schedule']),
                                    ],
                                    supports_check_mode=True)

        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:
            if 'vserver' in self.parameters:
                self.server = netapp_utils.setup_na_ontap_zapi(
                    module=self.module, vserver=self.parameters['vserver'])
            else:
                self.server = netapp_utils.setup_na_ontap_zapi(
                    module=self.module)
        return

    def get_snapshot_policy(self):
        """
        Checks to see if a snapshot policy exists or not
        :return: Return policy details if a snapshot policy exists, None if it doesn't
        """
        snapshot_obj = netapp_utils.zapi.NaElement("snapshot-policy-get-iter")
        # compose query
        query = netapp_utils.zapi.NaElement("query")
        snapshot_info_obj = netapp_utils.zapi.NaElement("snapshot-policy-info")
        snapshot_info_obj.add_new_child("policy", self.parameters['name'])
        if 'vserver' in self.parameters:
            snapshot_info_obj.add_new_child("vserver-name",
                                            self.parameters['vserver'])
        query.add_child_elem(snapshot_info_obj)
        snapshot_obj.add_child_elem(query)
        try:
            result = self.server.invoke_successfully(snapshot_obj, True)
            if result.get_child_by_name('num-records') and \
                    int(result.get_child_content('num-records')) == 1:
                snapshot_policy = result.get_child_by_name(
                    'attributes-list').get_child_by_name(
                        'snapshot-policy-info')
                current = {}
                current['name'] = snapshot_policy.get_child_content('policy')
                current['vserver'] = snapshot_policy.get_child_content(
                    'vserver-name')
                current[
                    'enabled'] = False if snapshot_policy.get_child_content(
                        'enabled').lower() == 'false' else True
                current['comment'] = snapshot_policy.get_child_content(
                    'comment') or ''
                current['schedule'], current['count'], current[
                    'snapmirror_label'] = [], [], []
                if snapshot_policy.get_child_by_name(
                        'snapshot-policy-schedules'):
                    for schedule in snapshot_policy[
                            'snapshot-policy-schedules'].get_children():
                        current['schedule'].append(
                            schedule.get_child_content('schedule'))
                        current['count'].append(
                            int(schedule.get_child_content('count')))
                        snapmirror_label = schedule.get_child_content(
                            'snapmirror-label')
                        if snapmirror_label is None or snapmirror_label == '-':
                            snapmirror_label = ''
                        current['snapmirror_label'].append(snapmirror_label)
                return current
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg=to_native(error),
                                  exception=traceback.format_exc())
        return None

    def validate_parameters(self):
        """
        Validate if each schedule has a count associated
        :return: None
        """
        if 'count' not in self.parameters or 'schedule' not in self.parameters or \
                len(self.parameters['count']) > 5 or len(self.parameters['schedule']) > 5 or \
                len(self.parameters['count']) < 1 or len(self.parameters['schedule']) < 1 or \
                len(self.parameters['count']) != len(self.parameters['schedule']):
            self.module.fail_json(
                msg="Error: A Snapshot policy must have at least 1 "
                "schedule and can have up to a maximum of 5 schedules, with a count "
                "representing the maximum number of Snapshot copies for each schedule"
            )

        if 'snapmirror_label' in self.parameters:
            if len(self.parameters['snapmirror_label']) != len(
                    self.parameters['schedule']):
                self.module.fail_json(
                    msg="Error: Each Snapshot Policy schedule must have an "
                    "accompanying SnapMirror Label")

    def modify_snapshot_policy(self, current):
        """
        Modifies an existing snapshot policy
        """
        # Set up required variables to modify snapshot policy
        options = {'policy': self.parameters['name']}
        modify = False

        # Set up optional variables to modify snapshot policy
        if 'enabled' in self.parameters and self.parameters[
                'enabled'] != current['enabled']:
            options['enabled'] = str(self.parameters['enabled'])
            modify = True
        if 'comment' in self.parameters and self.parameters[
                'comment'] != current['comment']:
            options['comment'] = self.parameters['comment']
            modify = True

        if modify:
            snapshot_obj = netapp_utils.zapi.NaElement.create_node_with_children(
                'snapshot-policy-modify', **options)
            try:
                self.server.invoke_successfully(snapshot_obj, True)
            except netapp_utils.zapi.NaApiError as error:
                self.module.fail_json(
                    msg='Error modifying snapshot policy %s: %s' %
                    (self.parameters['name'], to_native(error)),
                    exception=traceback.format_exc())

    def modify_snapshot_policy_schedules(self, current):
        """
        Modify existing schedules in snapshot policy
        :return: None
        """
        self.validate_parameters()

        delete_schedules, modify_schedules, add_schedules = [], [], []

        if 'snapmirror_label' in self.parameters:
            snapmirror_labels = self.parameters['snapmirror_label']
        else:
            # User hasn't supplied any snapmirror labels.
            snapmirror_labels = [None] * len(self.parameters['schedule'])

        # Identify schedules for deletion
        for schedule in current['schedule']:
            schedule = schedule.strip()
            if schedule not in [
                    item.strip() for item in self.parameters['schedule']
            ]:
                options = {'policy': current['name'], 'schedule': schedule}
                delete_schedules.append(options)

        # Identify schedules to be modified or added
        for schedule, count, snapmirror_label in zip(
                self.parameters['schedule'], self.parameters['count'],
                snapmirror_labels):
            schedule = schedule.strip()
            if snapmirror_label is not None:
                snapmirror_label = snapmirror_label.strip()

            options = {'policy': current['name'], 'schedule': schedule}

            if schedule in current['schedule']:
                # Schedule exists. Only modify if it has changed.
                modify = False
                schedule_index = current['schedule'].index(schedule)

                if count != current['count'][schedule_index]:
                    options['new-count'] = str(count)
                    modify = True

                if snapmirror_label is not None:
                    if snapmirror_label != current['snapmirror_label'][
                            schedule_index]:
                        options['new-snapmirror-label'] = snapmirror_label
                        modify = True

                if modify:
                    modify_schedules.append(options)
            else:
                # New schedule
                options['count'] = str(count)
                if snapmirror_label is not None and snapmirror_label != '':
                    options['snapmirror-label'] = snapmirror_label
                add_schedules.append(options)

        # Delete N-1 schedules no longer required. Must leave 1 schedule in policy
        # at any one time. Delete last one afterwards.
        while len(delete_schedules) > 1:
            options = delete_schedules.pop()
            self.modify_snapshot_policy_schedule(
                options, 'snapshot-policy-remove-schedule')

        # Modify schedules.
        while len(modify_schedules) > 0:
            options = modify_schedules.pop()
            self.modify_snapshot_policy_schedule(
                options, 'snapshot-policy-modify-schedule')

        # Add N-1 new schedules. Add last one after last schedule has been deleted.
        while len(add_schedules) > 1:
            options = add_schedules.pop()
            self.modify_snapshot_policy_schedule(
                options, 'snapshot-policy-add-schedule')

        # Delete last schedule no longer required.
        while len(delete_schedules) > 0:
            options = delete_schedules.pop()
            self.modify_snapshot_policy_schedule(
                options, 'snapshot-policy-remove-schedule')

        # Add last new schedule.
        while len(add_schedules) > 0:
            options = add_schedules.pop()
            self.modify_snapshot_policy_schedule(
                options, 'snapshot-policy-add-schedule')

    def modify_snapshot_policy_schedule(self, options, zapi):
        """
        Add, modify or remove a schedule to/from a snapshot policy
        """
        snapshot_obj = netapp_utils.zapi.NaElement.create_node_with_children(
            zapi, **options)
        try:
            self.server.invoke_successfully(snapshot_obj,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error modifying snapshot policy schedule %s: %s' %
                (self.parameters['name'], to_native(error)),
                exception=traceback.format_exc())

    def create_snapshot_policy(self):
        """
        Creates a new snapshot policy
        """
        # set up required variables to create a snapshot policy
        self.validate_parameters()
        options = {
            'policy': self.parameters['name'],
            'enabled': str(self.parameters['enabled']),
        }

        if 'snapmirror_label' in self.parameters:
            snapmirror_labels = self.parameters['snapmirror_label']
        else:
            # User hasn't supplied any snapmirror labels.
            snapmirror_labels = [None] * len(self.parameters['schedule'])

        # zapi attribute for first schedule is schedule1, second is schedule2 and so on
        positions = [
            str(i) for i in range(1,
                                  len(self.parameters['schedule']) + 1)
        ]
        for schedule, count, snapmirror_label, position in zip(
                self.parameters['schedule'], self.parameters['count'],
                snapmirror_labels, positions):
            schedule = schedule.strip()
            options['count' + position] = str(count)
            options['schedule' + position] = schedule
            if snapmirror_label is not None:
                snapmirror_label = snapmirror_label.strip()
                if snapmirror_label != '':
                    options['snapmirror-label' + position] = snapmirror_label
        snapshot_obj = netapp_utils.zapi.NaElement.create_node_with_children(
            'snapshot-policy-create', **options)

        # Set up optional variables to create a snapshot policy
        if self.parameters.get('comment'):
            snapshot_obj.add_new_child("comment", self.parameters['comment'])
        try:
            self.server.invoke_successfully(snapshot_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error creating snapshot policy %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def delete_snapshot_policy(self):
        """
        Deletes an existing snapshot policy
        """
        snapshot_obj = netapp_utils.zapi.NaElement("snapshot-policy-delete")

        # Set up required variables to delete a snapshot policy
        snapshot_obj.add_new_child("policy", self.parameters['name'])
        try:
            self.server.invoke_successfully(snapshot_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error deleting snapshot policy %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    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)

    def apply(self):
        """
        Check to see which play we should run
        """
        self.asup_log_for_cserver("na_ontap_snapshot_policy")
        current = self.get_snapshot_policy()
        modify = None
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if cd_action is None and self.parameters['state'] == 'present':
            # Don't sort schedule/count/snapmirror_label lists as it can
            # mess up the intended parameter order.
            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_snapshot_policy()
                elif cd_action == 'delete':
                    self.delete_snapshot_policy()
                if modify:
                    self.modify_snapshot_policy(current)
                    self.modify_snapshot_policy_schedules(current)
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapSVM(object):
    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'),
                 name=dict(required=True, type='str'),
                 from_name=dict(required=False, type='str'),
                 root_volume=dict(type='str'),
                 root_volume_aggregate=dict(type='str'),
                 root_volume_security_style=dict(
                     type='str', choices=['unix', 'ntfs', 'mixed', 'unified']),
                 allowed_protocols=dict(type='list'),
                 aggr_list=dict(type='list'),
                 ipspace=dict(type='str', required=False),
                 snapshot_policy=dict(type='str', required=False),
                 language=dict(type='str', required=False),
                 subtype=dict(choices=[
                     'default', 'dp_destination', 'sync_source',
                     'sync_destination'
                 ]),
                 comment=dict(type="str", required=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)

        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_vserver(self, vserver_name=None):
        """
        Checks if vserver exists.

        :return:
            vserver object if vserver found
            None if vserver is not found
        :rtype: object/None
        """
        if vserver_name is None:
            vserver_name = self.parameters['name']

        vserver_info = netapp_utils.zapi.NaElement('vserver-get-iter')
        query_details = netapp_utils.zapi.NaElement.create_node_with_children(
            'vserver-info', **{'vserver-name': vserver_name})

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

        result = self.server.invoke_successfully(vserver_info,
                                                 enable_tunneling=False)
        vserver_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')
            vserver_info = attributes_list.get_child_by_name('vserver-info')
            aggr_list = list()
            ''' vserver aggr-list can be empty by default'''
            get_list = vserver_info.get_child_by_name('aggr-list')
            if get_list is not None:
                aggregates = get_list.get_children()
                for aggr in aggregates:
                    aggr_list.append(aggr.get_content())

            protocols = list()
            '''allowed-protocols is not empty for data SVM, but is for node SVM'''
            allowed_protocols = vserver_info.get_child_by_name(
                'allowed-protocols')
            if allowed_protocols is not None:
                get_protocols = allowed_protocols.get_children()
                for protocol in get_protocols:
                    protocols.append(protocol.get_content())
            vserver_details = {
                'name':
                vserver_info.get_child_content('vserver-name'),
                'root_volume':
                vserver_info.get_child_content('root-volume'),
                'root_volume_aggregate':
                vserver_info.get_child_content('root-volume-aggregate'),
                'root_volume_security_style':
                vserver_info.get_child_content('root-volume-security-style'),
                'subtype':
                vserver_info.get_child_content('vserver-subtype'),
                'aggr_list':
                aggr_list,
                'language':
                vserver_info.get_child_content('language'),
                'snapshot_policy':
                vserver_info.get_child_content('snapshot-policy'),
                'allowed_protocols':
                protocols,
                'ipspace':
                vserver_info.get_child_content('ipspace'),
                'comment':
                vserver_info.get_child_content('comment')
            }
        return vserver_details

    def create_vserver(self):
        options = {'vserver-name': self.parameters['name']}
        self.add_parameter_to_dict(options, 'root_volume', 'root-volume')
        self.add_parameter_to_dict(options, 'root_volume_aggregate',
                                   'root-volume-aggregate')
        self.add_parameter_to_dict(options, 'root_volume_security_style',
                                   'root-volume-security-style')
        self.add_parameter_to_dict(options, 'language', 'language')
        self.add_parameter_to_dict(options, 'ipspace', 'ipspace')
        self.add_parameter_to_dict(options, 'snapshot_policy',
                                   'snapshot-policy')
        self.add_parameter_to_dict(options, 'subtype', 'vserver-subtype')
        self.add_parameter_to_dict(options, 'comment', 'comment')
        vserver_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'vserver-create', **options)
        try:
            self.server.invoke_successfully(vserver_create,
                                            enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as e:
            self.module.fail_json(msg='Error provisioning SVM %s: %s' %
                                  (self.parameters['name'], to_native(e)),
                                  exception=traceback.format_exc())
        # add allowed-protocols, aggr-list after creation,
        # since vserver-create doesn't allow these attributes during creation
        options = dict()
        for key in ('allowed_protocols', 'aggr_list'):
            if self.parameters.get(key):
                options[key] = self.parameters[key]
        if options:
            self.modify_vserver(options)

    def delete_vserver(self):
        vserver_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'vserver-destroy', **{'vserver-name': self.parameters['name']})

        try:
            self.server.invoke_successfully(vserver_delete,
                                            enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as e:
            self.module.fail_json(msg='Error deleting SVM %s: %s' %
                                  (self.parameters['name'], to_native(e)),
                                  exception=traceback.format_exc())

    def rename_vserver(self):
        vserver_rename = netapp_utils.zapi.NaElement.create_node_with_children(
            'vserver-rename', **{
                'vserver-name': self.parameters['from_name'],
                'new-name': self.parameters['name']
            })

        try:
            self.server.invoke_successfully(vserver_rename,
                                            enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as e:
            self.module.fail_json(msg='Error renaming SVM %s: %s' %
                                  (self.parameters['from_name'], to_native(e)),
                                  exception=traceback.format_exc())

    def modify_vserver(self, modify):
        '''
        Modify vserver.
        :param modify: list of modify attributes
        '''
        vserver_modify = netapp_utils.zapi.NaElement('vserver-modify')
        vserver_modify.add_new_child('vserver-name', self.parameters['name'])
        for attribute in modify:
            if attribute == 'language':
                vserver_modify.add_new_child('language',
                                             self.parameters['language'])
            if attribute == 'snapshot_policy':
                vserver_modify.add_new_child(
                    'snapshot_policy', self.parameters['snapshot_policy'])
            if attribute == 'comment':
                vserver_modify.add_new_child('comment',
                                             self.parameters['comment'])
            if attribute == 'allowed_protocols':
                allowed_protocols = netapp_utils.zapi.NaElement(
                    'allowed-protocols')
                for protocol in self.parameters['allowed_protocols']:
                    allowed_protocols.add_new_child('protocol', protocol)
                vserver_modify.add_child_elem(allowed_protocols)
            if attribute == 'aggr_list':
                aggregates = netapp_utils.zapi.NaElement('aggr-list')
                for aggr in self.parameters['aggr_list']:
                    aggregates.add_new_child('aggr-name', aggr)
                vserver_modify.add_child_elem(aggregates)
        try:
            self.server.invoke_successfully(vserver_modify,
                                            enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as e:
            self.module.fail_json(msg='Error modifying SVM %s: %s' %
                                  (self.parameters['name'], to_native(e)),
                                  exception=traceback.format_exc())

    def add_parameter_to_dict(self, adict, name, key=None, tostr=False):
        '''
        add defined parameter (not None) to adict using key.
        :param adict: a dictionary.
        :param name: name in self.parameters.
        :param key:  key in adict.
        :param tostr: boolean.
        '''
        if key is None:
            key = name
        if self.parameters.get(name) is not None:
            if tostr:
                adict[key] = str(self.parameters.get(name))
            else:
                adict[key] = self.parameters.get(name)

    def apply(self):
        '''Call create/modify/delete operations.'''
        self.asup_log_for_cserver("na_ontap_svm")
        current = self.get_vserver()
        cd_action, rename = None, None
        if self.parameters.get('from_name'):
            rename = self.na_helper.is_rename_action(
                self.get_vserver(self.parameters['from_name']), current)
        else:
            cd_action = self.na_helper.get_cd_action(current, self.parameters)
        modify = self.na_helper.get_modified_attributes(
            current, self.parameters)
        for attribute in modify:
            if attribute in [
                    'root_volume', 'root_volume_aggregate',
                    'root_volume_security_style', 'subtype', 'ipspace'
            ]:
                self.module.fail_json(
                    msg='Error modifying SVM %s: can not modify %s.' %
                    (self.parameters['name'], attribute))
            if attribute == 'language':
                # Ontap documentation uses C.UTF-8, but actually stores as c.utf_8.
                if self.parameters['language'].lower() == 'c.utf-8':
                    self.parameters['language'] = 'c.utf_8'
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if rename:
                    self.rename_vserver()
                # If rename is True, cd_action is None, but modify could be true or false.
                if cd_action == 'create':
                    self.create_vserver()
                elif cd_action == 'delete':
                    self.delete_vserver()
                elif modify:
                    self.modify_vserver(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)
class NetAppONTAPCifsShare(object):
    """
    Methods to create/delete/modify(path) CIFS share
    """

    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'),
            share_name=dict(required=True, type='str'),
            path=dict(required=False, type='str'),
            vserver=dict(required=True, type='str'),
            share_properties=dict(required=False, type='list'),
            symlink_properties=dict(required=False, type='list'),
            vscan_fileop_profile=dict(required=False, type='str', choices=['no_scan', 'standard', 'strict', 'writes_only'])
        ))

        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)

        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.get('vserver'))

    def get_cifs_share(self):
        """
        Return details about the cifs-share
        :param:
            name : Name of the cifs-share
        :return: Details about the cifs-share. None if not found.
        :rtype: dict
        """
        cifs_iter = netapp_utils.zapi.NaElement('cifs-share-get-iter')
        cifs_info = netapp_utils.zapi.NaElement('cifs-share')
        cifs_info.add_new_child('share-name', self.parameters.get('share_name'))
        cifs_info.add_new_child('vserver', self.parameters.get('vserver'))

        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(cifs_info)

        cifs_iter.add_child_elem(query)

        result = self.server.invoke_successfully(cifs_iter, True)

        return_value = None
        # check if query returns the expected cifs-share
        if result.get_child_by_name('num-records') and \
                int(result.get_child_content('num-records')) == 1:
            properties_list = []
            symlink_list = []
            cifs_attrs = result.get_child_by_name('attributes-list').\
                get_child_by_name('cifs-share')
            if cifs_attrs.get_child_by_name('share-properties'):
                properties_attrs = cifs_attrs['share-properties']
                if properties_attrs is not None:
                    properties_list = [property.get_content() for property in properties_attrs.get_children()]
            if cifs_attrs.get_child_by_name('symlink-properties'):
                symlink_attrs = cifs_attrs['symlink-properties']
                if symlink_attrs is not None:
                    symlink_list = [symlink.get_content() for symlink in symlink_attrs.get_children()]
            return_value = {
                'share': cifs_attrs.get_child_content('share-name'),
                'path': cifs_attrs.get_child_content('path'),
                'share_properties': properties_list,
                'symlink_properties': symlink_list
            }
            if cifs_attrs.get_child_by_name('vscan-fileop-profile'):
                return_value['vscan_fileop_profile'] = cifs_attrs['vscan-fileop-profile']

        return return_value

    def create_cifs_share(self):
        """
        Create CIFS share
        """
        options = {'share-name': self.parameters.get('share_name'),
                   'path': self.parameters.get('path')}
        cifs_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'cifs-share-create', **options)
        if self.parameters.get('share_properties'):
            property_attrs = netapp_utils.zapi.NaElement('share-properties')
            cifs_create.add_child_elem(property_attrs)
            for property in self.parameters.get('share_properties'):
                property_attrs.add_new_child('cifs-share-properties', property)
        if self.parameters.get('symlink_properties'):
            symlink_attrs = netapp_utils.zapi.NaElement('symlink-properties')
            cifs_create.add_child_elem(symlink_attrs)
            for symlink in self.parameters.get('symlink_properties'):
                symlink_attrs.add_new_child('cifs-share-symlink-properties', symlink)
        if self.parameters.get('vscan_fileop_profile'):
            fileop_attrs = netapp_utils.zapi.NaElement('vscan-fileop-profile')
            fileop_attrs.set_content(self.parameters['vscan_fileop_profile'])
            cifs_create.add_child_elem(fileop_attrs)

        try:
            self.server.invoke_successfully(cifs_create,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:

            self.module.fail_json(msg='Error creating cifs-share %s: %s'
                                  % (self.parameters.get('share_name'), to_native(error)),
                                  exception=traceback.format_exc())

    def delete_cifs_share(self):
        """
        Delete CIFS share
        """
        cifs_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'cifs-share-delete', **{'share-name': self.parameters.get('share_name')})

        try:
            self.server.invoke_successfully(cifs_delete,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error deleting cifs-share %s: %s'
                                  % (self.parameters.get('share_name'), to_native(error)),
                                  exception=traceback.format_exc())

    def modify_cifs_share(self):
        """
        modify path for the given CIFS share
        """
        options = {'share-name': self.parameters.get('share_name')}
        cifs_modify = netapp_utils.zapi.NaElement.create_node_with_children(
            'cifs-share-modify', **options)
        if self.parameters.get('path'):
            cifs_modify.add_new_child('path', self.parameters.get('path'))
        if self.parameters.get('share_properties'):
            property_attrs = netapp_utils.zapi.NaElement('share-properties')
            cifs_modify.add_child_elem(property_attrs)
            for property in self.parameters.get('share_properties'):
                property_attrs.add_new_child('cifs-share-properties', property)
        if self.parameters.get('symlink_properties'):
            symlink_attrs = netapp_utils.zapi.NaElement('symlink-properties')
            cifs_modify.add_child_elem(symlink_attrs)
            for property in self.parameters.get('symlink_properties'):
                symlink_attrs.add_new_child('cifs-share-symlink-properties', property)
        if self.parameters.get('vscan_fileop_profile'):
            fileop_attrs = netapp_utils.zapi.NaElement('vscan-fileop-profile')
            fileop_attrs.set_content(self.parameters['vscan_fileop_profile'])
            cifs_modify.add_child_elem(fileop_attrs)
        try:
            self.server.invoke_successfully(cifs_modify,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error modifying cifs-share %s:%s'
                                  % (self.parameters.get('share_name'), to_native(error)),
                                  exception=traceback.format_exc())

    def apply(self):
        '''Apply action to cifs share'''
        netapp_utils.ems_log_event("na_ontap_cifs", self.server)
        current = self.get_cifs_share()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if 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_cifs_share()
                elif cd_action == 'delete':
                    self.delete_cifs_share()
                elif modify:
                    self.modify_cifs_share()
        self.module.exit_json(changed=self.na_helper.changed)
예제 #21
0
class NetAppOntapVolumeAutosize(object):
    def __init__(self):
        self.use_rest = False
        # Volume_autosize returns KB and not B like Volume so values are shifted down 1
        self._size_unit_map = dict(
            k=1,
            m=1024,
            g=1024 ** 2,
            t=1024 ** 3,
        )
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(dict(
            volume=dict(required=True, type="str"),
            mode=dict(required=False, choices=['grow', 'grow_shrink', 'off']),
            vserver=dict(required=True, type='str'),
            grow_threshold_percent=dict(required=False, type='int'),
            increment_size=dict(required=False, type='str'),
            maximum_size=dict(required=False, type='str'),
            minimum_size=dict(required=False, type='str'),
            reset=dict(required=False, type='bool'),
            shrink_threshold_percent=dict(required=False, type='int')
        ))
        self.module = AnsibleModule(
            argument_spec=self.argument_spec,
            supports_check_mode=True,
            mutually_exclusive=[
                ['reset', 'maximum_size'],
                ['reset', 'increment_size'],
                ['reset', 'minimum_size'],
                ['reset', 'grow_threshold_percent'],
                ['reset', 'shrink_threshold_percent'],
                ['reset', 'mode']
            ]
        )
        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)
        # API should be used for ONTAP 9.6 or higher, ZAPI for lower version
        self.restApi = OntapRestAPI(self.module)
        if self.restApi.is_rest():
            self.use_rest = True
            # increment size and reset are not supported with rest api
            if self.parameters.get('increment_size'):
                self.module.fail_json(msg="Rest API does not support increment size, please switch to ZAPI")
            if self.parameters.get('reset'):
                self.module.fail_json(msg="Rest API does not support reset, please switch to ZAPI")
        else:
            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 get_volume_autosize(self, uuid=None):
        """
        Get volume_autosize information from the ONTAP system
        :return:
        """
        if self.use_rest:
            params = {'fields': 'autosize'}
            api = 'storage/volumes/' + uuid
            message, error = self.restApi.get(api, params)
            if error is not None:
                self.module.fail_json(msg="%s" % error)
            return self._create_get_volume_return(message['autosize'])
        else:
            volume_autosize_info = netapp_utils.zapi.NaElement('volume-autosize-get')
            volume_autosize_info.add_new_child('volume', self.parameters['volume'])
            try:
                result = self.server.invoke_successfully(volume_autosize_info, True)
            except netapp_utils.zapi.NaApiError as error:
                self.module.fail_json(msg='Error fetching volume autosize infor for %s : %s' % (self.parameters['volume'],
                                                                                                to_native(error)),
                                      exception=traceback.format_exc())
            return self._create_get_volume_return(result)

    def _create_get_volume_return(self, results):
        """
        Create a return value from volume-autosize-get info file
        :param results:
        :return:
        """
        return_value = {}
        if self.use_rest:
            if 'mode' in results:
                return_value['mode'] = results['mode']
            if 'grow_threshold' in results:
                return_value['grow_threshold_percent'] = results['grow_threshold']
            if 'maximum' in results:
                return_value['maximum_size'] = results['maximum']
            if 'minimum' in results:
                return_value['minimum_size'] = results['minimum']
            if 'shrink_threshold' in results:
                return_value['shrink_threshold_percent'] = results['shrink_threshold']
        else:
            if results.get_child_by_name('mode'):
                return_value['mode'] = results.get_child_content('mode')
            if results.get_child_by_name('grow-threshold-percent'):
                return_value['grow_threshold_percent'] = int(results.get_child_content('grow-threshold-percent'))
            if results.get_child_by_name('increment-size'):
                return_value['increment_size'] = results.get_child_content('increment-size')
            if results.get_child_by_name('maximum-size'):
                return_value['maximum_size'] = results.get_child_content('maximum-size')
            if results.get_child_by_name('minimum-size'):
                return_value['minimum_size'] = results.get_child_content('minimum-size')
            if results.get_child_by_name('shrink-threshold-percent'):
                return_value['shrink_threshold_percent'] = int(results.get_child_content('shrink-threshold-percent'))
        if return_value == {}:
            return_value = None
        return return_value

    def modify_volume_autosize(self, uuid=None):
        """
        Modify a Volumes autosize
        :return:
        """
        if self.use_rest:
            params = {}
            data = {}
            autosize = {}
            if self.parameters.get('mode'):
                autosize['mode'] = self.parameters['mode']
            if self.parameters.get('grow_threshold_percent'):
                autosize['grow_threshold'] = self.parameters['grow_threshold_percent']
            if self.parameters.get('maximum_size'):
                autosize['maximum'] = self.parameters['maximum_size']
            if self.parameters.get('minimum_size'):
                autosize['minimum'] = self.parameters['minimum_size']
            if self.parameters.get('shrink_threshold_percent'):
                autosize['shrink_threshold'] = self.parameters['shrink_threshold_percent']
            data['autosize'] = autosize
            api = "storage/volumes/" + uuid
            message, error = self.restApi.patch(api, data, params)
            if error is not None:
                self.module.fail_json(msg="%s" % error)

        else:
            volume_autosize_info = netapp_utils.zapi.NaElement('volume-autosize-set')
            volume_autosize_info.add_new_child('volume', self.parameters['volume'])
            if self.parameters.get('mode'):
                volume_autosize_info.add_new_child('mode', self.parameters['mode'])
            if self.parameters.get('grow_threshold_percent'):
                volume_autosize_info.add_new_child('grow-threshold-percent', str(self.parameters['grow_threshold_percent']))
            if self.parameters.get('increment_size'):
                volume_autosize_info.add_new_child('increment-size', self.parameters['increment_size'])
            if self.parameters.get('reset') is not None:
                volume_autosize_info.add_new_child('reset', str(self.parameters['reset']))
            if self.parameters.get('maximum_size'):
                volume_autosize_info.add_new_child('maximum-size', self.parameters['maximum_size'])
            if self.parameters.get('minimum_size'):
                volume_autosize_info.add_new_child('minimum-size', self.parameters['minimum_size'])
            if self.parameters.get('shrink_threshold_percent'):
                volume_autosize_info.add_new_child('shrink-threshold-percent', str(self.parameters['shrink_threshold_percent']))
            try:
                self.server.invoke_successfully(volume_autosize_info, True)
            except netapp_utils.zapi.NaApiError as error:
                self.module.fail_json(msg="Error modify volume autosize for %s: %s" % (self.parameters["volume"], to_native(error)),
                                      exception=traceback.format_exc())

    def modify_to_kb(self, converted_parameters):
        """
        Save a converted parameter
        :param converted_parameters: Dic of all parameters
        :return:
        """
        for attr in ['maximum_size', 'minimum_size', 'increment_size']:
            if converted_parameters.get(attr):
                if self.use_rest:
                    converted_parameters[attr] = self.convert_to_byte(attr, converted_parameters)
                else:
                    converted_parameters[attr] = str(self.convert_to_kb(attr, converted_parameters))
        return converted_parameters

    def convert_to_kb(self, variable, converted_parameters):
        """
        Convert a number 10m in to its correct KB size
        :param variable: the Parameter we are going to covert
        :param converted_parameters: Dic of all parameters
        :return:
        """
        if converted_parameters.get(variable)[-1] not in ['k', 'm', 'g', 't']:
            self.module.fail_json(msg="%s must end with a k, m, g or t" % variable)
        return self._size_unit_map[converted_parameters.get(variable)[-1]] * int(converted_parameters.get(variable)[:-1])

    def convert_to_byte(self, variable, converted_parameters):
        if converted_parameters.get(variable)[-1] not in ['k', 'm', 'g', 't']:
            self.module.fail_json(msg="%s must end with a k, m, g or t" % variable)
        return (self._size_unit_map[converted_parameters.get(variable)[-1]] * int(converted_parameters.get(variable)[:-1])) * 1024

    def get_volume_uuid(self):
        """
        Get a volume's UUID
        :return: uuid of the volume
        """
        params = {'fields': '*',
                  'name': self.parameters['volume'],
                  'svm.name': self.parameters['vserver']}
        api = "storage/volumes"
        message, error = self.restApi.get(api, params)
        if error is not None:
            self.module.fail_json(msg="%s" % error)
        return message['records'][0]['uuid']

    def apply(self):
        # TODO Logging for rest
        uuid = None
        if not self.use_rest:
            netapp_utils.ems_log_event("na_ontap_volume_autosize", self.server)
        if self.use_rest:
            # we only have the volume name, we need to the the uuid for the volume
            uuid = self.get_volume_uuid()
        current = self.get_volume_autosize(uuid=uuid)
        converted_parameters = copy.deepcopy(self.parameters)
        converted_parameters = self.modify_to_kb(converted_parameters)
        self.na_helper.get_modified_attributes(current, converted_parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                self.modify_volume_autosize(uuid=uuid)
        if self.parameters.get('reset') is True:
            self.modify_volume_autosize(uuid=uuid)
            self.na_helper.changed = True
        self.module.exit_json(changed=self.na_helper.changed)
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,
                            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')))

        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)
예제 #23
0
class NetAppONTAPNVMe(object):
    """
    Class with NVMe service 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'),
            status_admin=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)

        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 get_nvme(self):
        """
        Get current nvme details
        :return: dict if nvme exists, None otherwise
        """
        nvme_get = netapp_utils.zapi.NaElement('nvme-get-iter')
        query = {
            'query': {
                'nvme-target-service-info': {
                    'vserver': self.parameters['vserver']
                }
            }
        }
        nvme_get.translate_struct(query)
        try:
            result = self.server.invoke_successfully(nvme_get, enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error fetching nvme info: %s' % to_native(error),
                                  exception=traceback.format_exc())
        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')
            nvme_info = attributes_list.get_child_by_name('nvme-target-service-info')
            return_value = {'status_admin': nvme_info.get_child_content('is-available')}
            return return_value
        return None

    def create_nvme(self):
        """
        Create NVMe service
        """
        nvme_create = netapp_utils.zapi.NaElement('nvme-create')
        if self.parameters.get('status_admin') is not None:
            options = {'is-available': self.parameters['status_admin']}
            nvme_create.translate_struct(options)
        try:
            self.server.invoke_successfully(nvme_create, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error creating nvme for vserver %s: %s'
                                  % (self.parameters['vserver'], to_native(error)),
                                  exception=traceback.format_exc())

    def delete_nvme(self):
        """
        Delete NVMe service
        """
        nvme_delete = netapp_utils.zapi.NaElement('nvme-delete')
        try:
            self.server.invoke_successfully(nvme_delete, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error deleting nvme for vserver %s: %s'
                                  % (self.parameters['vserver'], to_native(error)),
                                  exception=traceback.format_exc())

    def modify_nvme(self, status=None):
        """
        Modify NVMe service
        """
        if status is None:
            status = self.parameters['status_admin']
        options = {'is-available': status}
        nvme_modify = netapp_utils.zapi.NaElement('nvme-modify')
        nvme_modify.translate_struct(options)
        try:
            self.server.invoke_successfully(nvme_modify, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error modifying nvme for vserver %s: %s'
                                  % (self.parameters['vserver'], to_native(error)),
                                  exception=traceback.format_exc())

    def apply(self):
        """
        Apply action to NVMe service
        """
        netapp_utils.ems_log_event("na_ontap_nvme", self.server)
        current = self.get_nvme()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if self.parameters.get('status_admin') is not None:
            self.parameters['status_admin'] = self.na_helper.get_value_for_bool(False, self.parameters['status_admin'])
            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_nvme()
                elif cd_action == 'delete':
                    # NVMe status_admin needs to be down before deleting it
                    self.modify_nvme('false')
                    self.delete_nvme()
                elif modify:
                    self.modify_nvme()

        self.module.exit_json(changed=self.na_helper.changed)
예제 #24
0
class NetAppOntapInterface(object):
    ''' object to describe  interface info '''
    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'),
            interface_name=dict(required=True, type='str'),
            home_node=dict(required=False, type='str', default=None),
            home_port=dict(required=False, type='str'),
            role=dict(required=False, type='str'),
            address=dict(required=False, type='str'),
            netmask=dict(required=False, type='str'),
            vserver=dict(required=True, type='str'),
            firewall_policy=dict(required=False, type='str', default=None),
            failover_policy=dict(required=False, type='str', default=None,
                                 choices=['disabled', 'system-defined',
                                          'local-only', 'sfo-partner-only', 'broadcast-domain-wide']),
            admin_status=dict(required=False, choices=['up', 'down']),
            subnet_name=dict(required=False, type='str'),
            is_auto_revert=dict(required=False, type='bool', default=None),
            protocols=dict(required=False, type='list'),
            force_subnet_association=dict(required=False, type='bool', default=None),
            dns_domain_name=dict(required=False, type='str'),
            listen_for_dns_query=dict(required=False, type='bool'),
            is_dns_update_enabled=dict(required=False, type='bool')
        ))

        self.module = AnsibleModule(
            argument_spec=self.argument_spec,
            mutually_exclusive=[
                ['subnet_name', 'address'],
                ['subnet_name', 'netmask']
            ],

            supports_check_mode=True
        )
        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 get_interface(self):
        """
        Return details about the interface
        :param:
            name : Name of the name of the interface

        :return: Details about the interface. None if not found.
        :rtype: dict
        """
        interface_info = netapp_utils.zapi.NaElement('net-interface-get-iter')
        interface_attributes = netapp_utils.zapi.NaElement('net-interface-info')
        interface_attributes.add_new_child('interface-name', self.parameters['interface_name'])
        interface_attributes.add_new_child('vserver', self.parameters['vserver'])
        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(interface_attributes)
        interface_info.add_child_elem(query)
        result = self.server.invoke_successfully(interface_info, True)
        return_value = None
        if result.get_child_by_name('num-records') and \
                int(result.get_child_content('num-records')) >= 1:

            interface_attributes = result.get_child_by_name('attributes-list').\
                get_child_by_name('net-interface-info')
            return_value = {
                'interface_name': self.parameters['interface_name'],
                'admin_status': interface_attributes['administrative-status'],
                'home_port': interface_attributes['home-port'],
                'home_node': interface_attributes['home-node'],
                'failover_policy': interface_attributes['failover-policy'].replace('_', '-'),
                'is_auto_revert': True if interface_attributes['is-auto-revert'] == 'true' else False,
            }
            if interface_attributes.get_child_by_name('address'):
                return_value['address'] = interface_attributes['address']
            if interface_attributes.get_child_by_name('netmask'):
                return_value['netmask'] = interface_attributes['netmask']
            if interface_attributes.get_child_by_name('firewall-policy'):
                return_value['firewall_policy'] = interface_attributes['firewall-policy']
            if interface_attributes.get_child_by_name('dns-domain-name') != 'none':
                return_value['dns_domain_name'] = interface_attributes['dns-domain-name']
            else:
                return_value['dns_domain_name'] = None
            if interface_attributes.get_child_by_name('listen-for-dns-query'):
                return_value['listen_for_dns_query'] = self.na_helper.get_value_for_bool(True, interface_attributes['listen-for-dns-query'])
            if interface_attributes.get_child_by_name('is-dns-update-enabled'):
                return_value['is_dns_update_enabled'] = self.na_helper.get_value_for_bool(True, interface_attributes['is-dns-update-enabled'])
        return return_value

    @staticmethod
    def set_options(options, parameters):
        """ set attributes for create or modify """
        if parameters.get('home_port') is not None:
            options['home-port'] = parameters['home_port']
        if parameters.get('subnet_name') is not None:
            options['subnet-name'] = parameters['subnet_name']
        if parameters.get('address') is not None:
            options['address'] = parameters['address']
        if parameters.get('netmask') is not None:
            options['netmask'] = parameters['netmask']
        if parameters.get('failover_policy') is not None:
            options['failover-policy'] = parameters['failover_policy']
        if parameters.get('firewall_policy') is not None:
            options['firewall-policy'] = parameters['firewall_policy']
        if parameters.get('is_auto_revert') is not None:
            options['is-auto-revert'] = 'true' if parameters['is_auto_revert'] is True else 'false'
        if parameters.get('admin_status') is not None:
            options['administrative-status'] = parameters['admin_status']
        if parameters.get('force_subnet_association') is not None:
            options['force-subnet-association'] = 'true' if parameters['force_subnet_association'] else 'false'
        if parameters.get('dns_domain_name') is not None:
            options['dns-domain-name'] = parameters['dns_domain_name']
        if parameters.get('listen_for_dns_query') is not None:
            options['listen-for-dns-query'] = str(parameters['listen_for_dns_query'])
        if parameters.get('is_dns_update_enabled') is not None:
            options['is-dns-update-enabled'] = str(parameters['is_dns_update_enabled'])

    def set_protocol_option(self, required_keys):
        """ set protocols for create """
        if self.parameters.get('protocols') is not None:
            data_protocols_obj = netapp_utils.zapi.NaElement('data-protocols')
            for protocol in self.parameters.get('protocols'):
                if protocol.lower() in ['fc-nvme', 'fcp']:
                    if 'address' in required_keys:
                        required_keys.remove('address')
                    if 'home_port' in required_keys:
                        required_keys.remove('home_port')
                    if 'netmask' in required_keys:
                        required_keys.remove('netmask')
                    not_required_params = set(['address', 'netmask', 'firewall_policy'])
                    if not not_required_params.isdisjoint(set(self.parameters.keys())):
                        self.module.fail_json(msg='Error: Following parameters for creating interface are not supported'
                                                  ' for data-protocol fc-nvme: %s' % ', '.join(not_required_params))
                data_protocols_obj.add_new_child('data-protocol', protocol)
            return data_protocols_obj
        return None

    def get_home_node_for_cluster(self):
        ''' get the first node name from this cluster '''
        get_node = netapp_utils.zapi.NaElement('cluster-node-get-iter')
        attributes = {
            'query': {
                'cluster-node-info': {}
            }
        }
        get_node.translate_struct(attributes)
        try:
            result = self.server.invoke_successfully(get_node, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as exc:
            self.module.fail_json(msg='Error fetching node for interface %s: %s' %
                                  (self.parameters['interface_name'], to_native(exc)),
                                  exception=traceback.format_exc())
        if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
            attributes = result.get_child_by_name('attributes-list')
            return attributes.get_child_by_name('cluster-node-info').get_child_content('node-name')
        return None

    def validate_create_parameters(self, keys):
        '''
            Validate if required parameters for create are present.
            Parameter requirement might vary based on given data-protocol.
            :return: None
        '''
        if self.parameters.get('home_node') is None:
            node = self.get_home_node_for_cluster()
            if node is not None:
                self.parameters['home_node'] = node
        # validate if mandatory parameters are present for create
        if not keys.issubset(set(self.parameters.keys())) and self.parameters.get('subnet_name') is None:
            self.module.fail_json(msg='Error: Missing one or more required parameters for creating interface: %s'
                                      % ', '.join(keys))
        # if role is intercluster, protocol cannot be specified
        if self.parameters['role'] == "intercluster" and self.parameters.get('protocols') is not None:
            self.module.fail_json(msg='Error: Protocol cannot be specified for intercluster role,'
                                      'failed to create interface')

    def create_interface(self):
        ''' calling zapi to create interface '''
        required_keys = set(['role', 'home_port'])
        data_protocols_obj = None
        if self.parameters.get('subnet_name') is None:
            required_keys.add('address')
            required_keys.add('netmask')
        data_protocols_obj = self.set_protocol_option(required_keys)
        self.validate_create_parameters(required_keys)

        options = {'interface-name': self.parameters['interface_name'],
                   'role': self.parameters['role'],
                   'home-node': self.parameters.get('home_node'),
                   'vserver': self.parameters['vserver']}
        NetAppOntapInterface.set_options(options, self.parameters)
        interface_create = netapp_utils.zapi.NaElement.create_node_with_children('net-interface-create', **options)
        if data_protocols_obj is not None:
            interface_create.add_child_elem(data_protocols_obj)
        try:
            self.server.invoke_successfully(interface_create, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as exc:
            self.module.fail_json(msg='Error Creating interface %s: %s' %
                                  (self.parameters['interface_name'], to_native(exc)), exception=traceback.format_exc())

    def delete_interface(self, current_status):
        ''' calling zapi to delete interface '''
        if current_status == 'up':
            self.parameters['admin_status'] = 'down'
            self.modify_interface({'admin_status': 'down'})

        interface_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'net-interface-delete', **{'interface-name': self.parameters['interface_name'],
                                       'vserver': self.parameters['vserver']})
        try:
            self.server.invoke_successfully(interface_delete, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as exc:
            self.module.fail_json(msg='Error deleting interface %s: %s' % (self.parameters['interface_name'], to_native(exc)),
                                  exception=traceback.format_exc())

    def modify_interface(self, modify):
        """
        Modify the interface.
        """
        options = {'interface-name': self.parameters['interface_name'],
                   'vserver': self.parameters['vserver']
                   }
        NetAppOntapInterface.set_options(options, modify)
        interface_modify = netapp_utils.zapi.NaElement.create_node_with_children('net-interface-modify', **options)
        try:
            self.server.invoke_successfully(interface_modify, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as err:
            self.module.fail_json(msg='Error modifying interface %s: %s' % (self.parameters['interface_name'],
                                  to_native(err)), 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_interface", cserver)

    def apply(self):
        ''' calling all interface features '''
        self.autosupport_log()
        current = self.get_interface()
        # rename and create are mutually exclusive
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        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_interface()
                elif cd_action == 'delete':
                    self.delete_interface(current['admin_status'])
                elif modify:
                    self.modify_interface(modify)
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapQTree(object):
    '''Class with qtree operations'''
    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'),
                name=dict(required=True, type='str'),
                from_name=dict(required=False, type='str'),
                flexvol_name=dict(type='str'),
                vserver=dict(required=True, type='str'),
                export_policy=dict(required=False, type='str'),
                security_style=dict(required=False,
                                    choices=['unix', 'ntfs', 'mixed']),
                oplocks=dict(required=False, choices=['enabled', 'disabled']),
                unix_permissions=dict(required=False, type='str'),
            ))

        self.module = AnsibleModule(argument_spec=self.argument_spec,
                                    required_if=[('state', 'present',
                                                  ['flexvol_name'])],
                                    supports_check_mode=True)
        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'])

    def get_qtree(self, name=None):
        """
        Checks if the qtree exists.
        :param:
            name : qtree name
        :return:
            Details about the qtree
            False if qtree is not found
        :rtype: bool
        """
        if name is None:
            name = self.parameters['name']

        qtree_list_iter = netapp_utils.zapi.NaElement('qtree-list-iter')
        query_details = netapp_utils.zapi.NaElement.create_node_with_children(
            'qtree-info', **{
                'vserver': self.parameters['vserver'],
                'volume': self.parameters['flexvol_name'],
                'qtree': name
            })
        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(query_details)
        qtree_list_iter.add_child_elem(query)
        result = self.server.invoke_successfully(qtree_list_iter,
                                                 enable_tunneling=True)
        return_q = None
        if (result.get_child_by_name('num-records')
                and int(result.get_child_content('num-records')) >= 1):
            return_q = {
                'export_policy':
                result['attributes-list']['qtree-info']['export-policy'],
                'unix_permissions':
                result['attributes-list']['qtree-info']['mode'],
                'oplocks':
                result['attributes-list']['qtree-info']['oplocks'],
                'security_style':
                result['attributes-list']['qtree-info']['security-style']
            }

        return return_q

    def create_qtree(self):
        """
        Create a qtree
        """
        options = {
            'qtree': self.parameters['name'],
            'volume': self.parameters['flexvol_name']
        }
        if self.parameters.get('export_policy'):
            options['export-policy'] = self.parameters['export_policy']
        if self.parameters.get('security_style'):
            options['security-style'] = self.parameters['security_style']
        if self.parameters.get('oplocks'):
            options['oplocks'] = self.parameters['oplocks']
        if self.parameters.get('unix_permissions'):
            options['mode'] = self.parameters['unix_permissions']
        qtree_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'qtree-create', **options)
        try:
            self.server.invoke_successfully(qtree_create,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error provisioning qtree %s: %s" %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def delete_qtree(self):
        """
        Delete a qtree
        """
        path = '/vol/%s/%s' % (self.parameters['flexvol_name'],
                               self.parameters['name'])
        qtree_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'qtree-delete', **{'qtree': path})

        try:
            self.server.invoke_successfully(qtree_delete,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error deleting qtree %s: %s" %
                                  (path, to_native(error)),
                                  exception=traceback.format_exc())

    def rename_qtree(self):
        """
        Rename a qtree
        """
        path = '/vol/%s/%s' % (self.parameters['flexvol_name'],
                               self.parameters['from_name'])
        new_path = '/vol/%s/%s' % (self.parameters['flexvol_name'],
                                   self.parameters['name'])
        qtree_rename = netapp_utils.zapi.NaElement.create_node_with_children(
            'qtree-rename', **{
                'qtree': path,
                'new-qtree-name': new_path
            })

        try:
            self.server.invoke_successfully(qtree_rename,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg="Error renaming qtree %s: %s" %
                (self.parameters['from_name'], to_native(error)),
                exception=traceback.format_exc())

    def modify_qtree(self):
        """
        Modify a qtree
        """
        options = {
            'qtree': self.parameters['name'],
            'volume': self.parameters['flexvol_name']
        }
        if self.parameters.get('export_policy'):
            options['export-policy'] = self.parameters['export_policy']
        if self.parameters.get('security_style'):
            options['security-style'] = self.parameters['security_style']
        if self.parameters.get('oplocks'):
            options['oplocks'] = self.parameters['oplocks']
        if self.parameters.get('unix_permissions'):
            options['mode'] = self.parameters['unix_permissions']
        qtree_modify = netapp_utils.zapi.NaElement.create_node_with_children(
            'qtree-modify', **options)
        try:
            self.server.invoke_successfully(qtree_modify,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error modifying qtree %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def apply(self):
        '''Call create/delete/modify/rename operations'''
        netapp_utils.ems_log_event("na_ontap_qtree", self.server)
        current = self.get_qtree()
        rename, cd_action, modify = None, None, None
        if self.parameters.get('from_name'):
            rename = self.na_helper.is_rename_action(
                self.get_qtree(self.parameters['from_name']), current)
        else:
            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 rename:
                    self.rename_qtree()
                if cd_action == 'create':
                    self.create_qtree()
                elif cd_action == 'delete':
                    self.delete_qtree()
                elif modify:
                    self.modify_qtree()
        self.module.exit_json(changed=self.na_helper.changed)