예제 #1
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']),
            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:
            True if user found
            False if user is not found
        :rtype: bool
        """
        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')
                }
                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 False
            # Error 16043 denotes the user existing, but the application missing
            elif to_native(error.code) == "16043":
                return False
            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 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 apply(self):
        create_delete_decision = {}
        modify = {}
        netapp_utils.ems_log_event("na_ontap_user", self.server)
        for application in self.parameters['applications']:
            current = self.get_user(application)
            cd_action = self.na_helper.get_cd_action(current, self.parameters)
            if cd_action is not None:
                create_delete_decision[application] = cd_action
        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
            current = self.get_user()
            if current is not None:
                current['lock_user'] = self.na_helper.get_value_for_bool(True, current['lock_user'])
            modify = self.na_helper.get_modified_attributes(current, self.parameters)

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if create_delete_decision:
                    for cd_action in create_delete_decision:
                        if create_delete_decision[cd_action] == 'create':
                            self.create_user(cd_action)
                        elif create_delete_decision[cd_action] == 'delete':
                            self.delete_user(cd_action)
                elif modify:
                    if self.parameters.get('lock_user'):
                        self.lock_given_user()
                    else:
                        self.unlock_given_user()
                elif 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)
예제 #2
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(reuired=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_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)
예제 #3
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 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)
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)
예제 #6
0
class NetAppOntapUnixGroup(object):
    """
    Common operations to manage UNIX groups
    """

    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(dict(
            state=dict(required=False, 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'),
        ))

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

    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):
        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 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)
예제 #7
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(
            state=dict(type='str', default='present', choices=['absent', 'present']),
            vserver=dict(type='str', required=True),
            status_admin=dict(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))
        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)))

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

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

    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)