예제 #1
0
class NetAppONTAPSvnOptions(object):
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(name=dict(required=False, type="str", default=None),
                 value=dict(required=False, type='str', default=None),
                 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 set_options(self):
        """
        Set a specific option
        :return: None
        """
        option_obj = netapp_utils.zapi.NaElement("options-set")
        option_obj.add_new_child('name', self.parameters['name'])
        option_obj.add_new_child('value', self.parameters['value'])
        try:
            result = self.server.invoke_successfully(option_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error setting options: %s" %
                                  to_native(error),
                                  exception=traceback.format_exc())

    def list_options(self):
        """
        List all Options on the Vserver
        :return: None
        """
        option_obj = netapp_utils.zapi.NaElement("options-list-info")
        try:
            result = self.server.invoke_successfully(option_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error getting options: %s" %
                                  to_native(error),
                                  exception=traceback.format_exc())

    def is_option_set(self):
        """
        Checks to see if an option is set or not
        :return: If option is set return True, else return False
        """
        option_obj = netapp_utils.zapi.NaElement("options-get-iter")
        options_info = netapp_utils.zapi.NaElement("option-info")
        if self.parameters.get('name') is not None:
            options_info.add_new_child("name", self.parameters['name'])
        if self.parameters.get('value') is not None:
            options_info.add_new_child("value", self.parameters['value'])
        if "vserver" in self.parameters.keys():
            if self.parameters['vserver'] is not None:
                options_info.add_new_child("vserver",
                                           self.parameters['vserver'])
        query = netapp_utils.zapi.NaElement("query")
        query.add_child_elem(options_info)
        option_obj.add_child_elem(query)
        try:
            result = self.server.invoke_successfully(option_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error finding option: %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 True
        return False

    def apply(self):
        changed = False
        netapp_utils.ems_log_event("na_ontap_svm_options", self.server)
        is_set = self.is_option_set()
        if not is_set:
            self.set_options()
            changed = True
        self.module.exit_json(changed=changed)
예제 #2
0
class NetAppOntapQosPolicyGroup(object):
    """
    Create, delete, modify and rename a policy group.
    """
    def __init__(self):
        """
        Initialize the Ontap qos policy group class.
        """
        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'),
                 from_name=dict(required=False, type='str'),
                 vserver=dict(required=True, type='str'),
                 max_throughput=dict(required=False, type='str'),
                 min_throughput=dict(required=False, type='str'),
                 force=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)

        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_policy_group(self, policy_group_name=None):
        """
        Return details of a policy group.
        :param policy_group_name: policy group name
        :return: policy group details.
        :rtype: dict.
        """
        if policy_group_name is None:
            policy_group_name = self.parameters['name']
        policy_group_get_iter = netapp_utils.zapi.NaElement(
            'qos-policy-group-get-iter')
        policy_group_info = netapp_utils.zapi.NaElement(
            'qos-policy-group-info')
        policy_group_info.add_new_child('policy-group', policy_group_name)
        policy_group_info.add_new_child('vserver', self.parameters['vserver'])
        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(policy_group_info)
        policy_group_get_iter.add_child_elem(query)
        result = self.server.invoke_successfully(policy_group_get_iter, True)
        policy_group_detail = None

        if result.get_child_by_name('num-records') and int(
                result.get_child_content('num-records')) == 1:
            policy_info = result.get_child_by_name(
                'attributes-list').get_child_by_name('qos-policy-group-info')

            policy_group_detail = {
                'name': policy_info.get_child_content('policy-group'),
                'vserver': policy_info.get_child_content('vserver'),
                'max_throughput':
                policy_info.get_child_content('max-throughput'),
                'min_throughput':
                policy_info.get_child_content('min-throughput')
            }
        return policy_group_detail

    def create_policy_group(self):
        """
        create a policy group name.
        """
        policy_group = netapp_utils.zapi.NaElement('qos-policy-group-create')
        policy_group.add_new_child('policy-group', self.parameters['name'])
        policy_group.add_new_child('vserver', self.parameters['vserver'])
        if self.parameters.get('max_throughput'):
            policy_group.add_new_child('max-throughput',
                                       self.parameters['max_throughput'])
        if self.parameters.get('min_throughput'):
            policy_group.add_new_child('min-throughput',
                                       self.parameters['min_throughput'])
        try:
            self.server.invoke_successfully(policy_group, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error creating qos policy group %s: %s' %
                (self.parameters['name'], to_native(error)),
                exception=traceback.format_exc())

    def delete_policy_group(self, policy_group=None):
        """
        delete an existing policy group.
        :param policy_group: policy group name.
        """
        if policy_group is None:
            policy_group = self.parameters['name']
        policy_group_obj = netapp_utils.zapi.NaElement(
            'qos-policy-group-delete')
        policy_group_obj.add_new_child('policy-group', policy_group)
        if self.parameters.get('force'):
            policy_group_obj.add_new_child('force',
                                           str(self.parameters['force']))
        try:
            self.server.invoke_successfully(policy_group_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error deleting qos policy group %s: %s' %
                (policy_group, to_native(error)),
                exception=traceback.format_exc())

    def modify_policy_group(self):
        """
        Modify policy group.
        """
        policy_group_obj = netapp_utils.zapi.NaElement(
            'qos-policy-group-modify')
        policy_group_obj.add_new_child('policy-group', self.parameters['name'])
        if self.parameters.get('max_throughput'):
            policy_group_obj.add_new_child('max-throughput',
                                           self.parameters['max_throughput'])
        if self.parameters.get('min_throughput'):
            policy_group_obj.add_new_child('min-throughput',
                                           self.parameters['min_throughput'])
        try:
            self.server.invoke_successfully(policy_group_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error modifying qos policy group %s: %s' %
                (self.parameters['name'], to_native(error)),
                exception=traceback.format_exc())

    def rename_policy_group(self):
        """
        Rename policy group name.
        """
        rename_obj = netapp_utils.zapi.NaElement('qos-policy-group-rename')
        rename_obj.add_new_child('new-name', self.parameters['name'])
        rename_obj.add_new_child('policy-group-name',
                                 self.parameters['from_name'])
        try:
            self.server.invoke_successfully(rename_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error renaming qos policy group %s: %s' %
                (self.parameters['from_name'], to_native(error)),
                exception=traceback.format_exc())

    def modify_helper(self, modify):
        """
        helper method to modify policy group.
        :param modify: modified attributes.
        """
        for attribute in modify.keys():
            if attribute in ['max_throughput', 'min_throughput']:
                self.modify_policy_group()

    def apply(self):
        """
        Run module based on playbook
        """
        self.asup_log_for_cserver("na_ontap_qos_policy_group")
        current = self.get_policy_group()
        rename, cd_action = None, None
        if self.parameters.get('from_name'):
            rename = self.na_helper.is_rename_action(
                self.get_policy_group(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)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if rename:
                    self.rename_policy_group()
                if cd_action == 'create':
                    self.create_policy_group()
                elif cd_action == 'delete':
                    self.delete_policy_group()
                elif modify:
                    self.modify_helper(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 NetAppOntapLUNCopy(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'],
                           default='present'),
                destination_vserver=dict(required=True, type='str'),
                destination_path=dict(required=True, type='str'),
                source_path=dict(required=True, type='str'),
                source_vserver=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,
                vserver=self.parameters['destination_vserver'])

    def get_lun(self):
        """
           Check if the LUN exists

        :return: true is it exists, false otherwise
        :rtype: bool
        """

        return_value = False
        lun_info = netapp_utils.zapi.NaElement('lun-get-iter')
        query_details = netapp_utils.zapi.NaElement('lun-info')

        query_details.add_new_child('path',
                                    self.parameters['destination_path'])
        query_details.add_new_child('vserver',
                                    self.parameters['destination_vserver'])

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

        lun_info.add_child_elem(query)
        try:
            result = self.server.invoke_successfully(lun_info, True)

        except netapp_utils.zapi.NaApiError as e:
            self.module.fail_json(
                msg="Error getting lun  info %s for  verver %s: %s" %
                (self.parameters['destination_path'],
                 self.parameters['destination_vserver'], to_native(e)),
                exception=traceback.format_exc())

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

    def copy_lun(self):
        """
        Copy LUN with requested path and vserver
        """
        lun_copy = netapp_utils.zapi.NaElement.create_node_with_children(
            'lun-copy-start',
            **{'source-vserver': self.parameters['source_vserver']})

        path_obj = netapp_utils.zapi.NaElement('paths')
        pair = netapp_utils.zapi.NaElement('lun-path-pair')
        pair.add_new_child('destination-path',
                           self.parameters['destination_path'])
        pair.add_new_child('source-path', self.parameters['source_path'])
        path_obj.add_child_elem(pair)
        lun_copy.add_child_elem(path_obj)

        try:
            self.server.invoke_successfully(lun_copy, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as e:
            self.module.fail_json(
                msg="Error copying lun from %s to  vserver %s: %s" %
                (self.parameters['source_vserver'],
                 self.parameters['destination_vserver'], to_native(e)),
                exception=traceback.format_exc())

    def apply(self):

        netapp_utils.ems_log_event("na_ontap_lun_copy", self.server)
        if self.get_lun():  # lun already exists at destination
            changed = False
        else:
            changed = True
            if self.module.check_mode:
                pass
            else:
                # need to copy lun
                if self.parameters['state'] == 'present':
                    self.copy_lun()

        self.module.exit_json(changed=changed)
예제 #4
0
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)
예제 #5
0
class ElementSWClusterConfig(object):
    """
    Element Software Configure Element SW Cluster
    """
    def __init__(self):
        self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()

        self.argument_spec.update(
            dict(modify_cluster_full_threshold=dict(
                type='dict',
                options=dict(stage2_aware_threshold=dict(type='int',
                                                         default=None),
                             stage3_block_threshold_percent=dict(type='int',
                                                                 default=None),
                             max_metadata_over_provision_factor=dict(
                                 type='int', default=None))),
                 encryption_at_rest=dict(type='str',
                                         choices=['present', 'absent']),
                 set_ntp_info=dict(type='dict',
                                   options=dict(
                                       broadcastclient=dict(type='bool',
                                                            default=False),
                                       ntp_servers=dict(type='list'))),
                 enable_virtual_volumes=dict(type='bool', default=True)))

        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_SF_SDK is False:
            self.module.fail_json(
                msg="Unable to import the SolidFire Python SDK")
        else:
            self.sfe = netapp_utils.create_sf_connection(module=self.module)

    def get_ntp_details(self):
        """
        get ntp info
        """
        # Get ntp details
        ntp_details = self.sfe.get_ntp_info()
        return ntp_details

    def cmp(self, provided_ntp_servers, existing_ntp_servers):
        # As python3 doesn't have default cmp function, defining manually to provide same functionality.
        return (provided_ntp_servers > existing_ntp_servers) - (
            provided_ntp_servers < existing_ntp_servers)

    def get_cluster_details(self):
        """
        get cluster info
        """
        cluster_details = self.sfe.get_cluster_info()
        return cluster_details

    def get_vvols_status(self):
        """
        get vvols status
        """
        feature_status = self.sfe.get_feature_status(feature='vvols')
        if feature_status is not None:
            return feature_status.features[0].enabled
        return None

    def get_cluster_full_threshold_status(self):
        """
        get cluster full threshold
        """
        cluster_full_threshold_status = self.sfe.get_cluster_full_threshold()
        return cluster_full_threshold_status

    def setup_ntp_info(self, servers, broadcastclient=None):
        """
        configure ntp
        """
        # Set ntp servers
        try:
            self.sfe.set_ntp_info(servers, broadcastclient)
        except Exception as exception_object:
            self.module.fail_json(msg='Error configuring ntp %s' %
                                  (to_native(exception_object)),
                                  exception=traceback.format_exc())

    def set_encryption_at_rest(self, state=None):
        """
        enable/disable encryption at rest
        """
        try:
            if state == 'present':
                encryption_state = 'enable'
                self.sfe.enable_encryption_at_rest()
            elif state == 'absent':
                encryption_state = 'disable'
                self.sfe.disable_encryption_at_rest()
        except Exception as exception_object:
            self.module.fail_json(
                msg='Failed to %s rest encryption %s' %
                (encryption_state, to_native(exception_object)),
                exception=traceback.format_exc())

    def enable_feature(self, feature):
        """
        enable feature
        """
        try:
            self.sfe.enable_feature(feature=feature)
        except Exception as exception_object:
            self.module.fail_json(msg='Error enabling %s %s' %
                                  (feature, to_native(exception_object)),
                                  exception=traceback.format_exc())

    def set_cluster_full_threshold(self,
                                   stage2_aware_threshold=None,
                                   stage3_block_threshold_percent=None,
                                   max_metadata_over_provision_factor=None):
        """
        modify cluster full threshold
        """
        try:
            self.sfe.modify_cluster_full_threshold(
                stage2_aware_threshold=stage2_aware_threshold,
                stage3_block_threshold_percent=stage3_block_threshold_percent,
                max_metadata_over_provision_factor=
                max_metadata_over_provision_factor)
        except Exception as exception_object:
            self.module.fail_json(
                msg='Failed to modify cluster full threshold %s' %
                (to_native(exception_object)),
                exception=traceback.format_exc())

    def apply(self):
        """
        Cluster configuration
        """
        changed = False
        result_message = None

        if self.parameters.get('modify_cluster_full_threshold') is not None:
            # get cluster full threshold
            cluster_full_threshold_details = self.get_cluster_full_threshold_status(
            )
            # maxMetadataOverProvisionFactor
            current_mmopf = cluster_full_threshold_details.max_metadata_over_provision_factor
            # stage3BlockThresholdPercent
            current_s3btp = cluster_full_threshold_details.stage3_block_threshold_percent
            # stage2AwareThreshold
            current_s2at = cluster_full_threshold_details.stage2_aware_threshold

            # is cluster full threshold state change required?
            if self.parameters.get("modify_cluster_full_threshold")['max_metadata_over_provision_factor'] is not None and \
                    current_mmopf != self.parameters['modify_cluster_full_threshold']['max_metadata_over_provision_factor'] or \
                    self.parameters.get("modify_cluster_full_threshold")['stage3_block_threshold_percent'] is not None and \
                    current_s3btp != self.parameters['modify_cluster_full_threshold']['stage3_block_threshold_percent'] or \
                    self.parameters.get("modify_cluster_full_threshold")['stage2_aware_threshold'] is not None and \
                    current_s2at != self.parameters['modify_cluster_full_threshold']['stage2_aware_threshold']:
                changed = True
                self.set_cluster_full_threshold(
                    self.parameters['modify_cluster_full_threshold']
                    ['stage2_aware_threshold'],
                    self.parameters['modify_cluster_full_threshold']
                    ['stage3_block_threshold_percent'],
                    self.parameters['modify_cluster_full_threshold']
                    ['max_metadata_over_provision_factor'])

        if self.parameters.get('encryption_at_rest') is not None:
            # get all cluster info
            cluster_info = self.get_cluster_details()
            # register rest state
            current_encryption_at_rest_state = cluster_info.cluster_info.encryption_at_rest_state

            # is encryption state change required?
            if current_encryption_at_rest_state == 'disabled' and self.parameters['encryption_at_rest'] == 'present' or \
               current_encryption_at_rest_state == 'enabled' and self.parameters['encryption_at_rest'] == 'absent':
                changed = True
                self.set_encryption_at_rest(
                    self.parameters['encryption_at_rest'])

        if self.parameters.get('set_ntp_info') is not None:
            # get all ntp details
            ntp_details = self.get_ntp_details()
            # register list of ntp servers
            ntp_servers = ntp_details.servers
            # broadcastclient
            broadcast_client = ntp_details.broadcastclient

            # has either the broadcastclient or the ntp server list changed?

            if self.parameters.get('set_ntp_info')['broadcastclient'] != broadcast_client or \
               self.cmp(self.parameters.get('set_ntp_info')['ntp_servers'], ntp_servers) != 0:
                changed = True
                self.setup_ntp_info(
                    self.parameters.get('set_ntp_info')['ntp_servers'],
                    self.parameters.get('set_ntp_info')['broadcastclient'])

        if self.parameters.get('enable_virtual_volumes') is not None:
            # check vvols status
            current_vvols_status = self.get_vvols_status()

            # has the vvols state changed?
            if current_vvols_status is False and self.parameters.get(
                    'enable_virtual_volumes') is True:
                changed = True
                self.enable_feature('vvols')
            elif current_vvols_status is True and self.parameters.get(
                    'enable_virtual_volumes') is not True:
                # vvols, once enabled, cannot be disabled
                self.module.fail_json(
                    msg='Error disabling vvols: this feature cannot be undone')

        if self.module.check_mode is True:
            result_message = "Check mode, skipping changes"
        self.module.exit_json(changed=changed, msg=result_message)
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)
예제 #7
0
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)
예제 #8
0
class NetAppOntapIfGrp(object):
    """
        Create, Modifies and Destroys a IfGrp
    """
    def __init__(self):
        """
            Initialize the Ontap IfGrp 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'),
                distribution_function=dict(
                    required=False,
                    type='str',
                    choices=['mac', 'ip', 'sequential', 'port']),
                name=dict(required=True, type='str'),
                mode=dict(required=False, type='str'),
                node=dict(required=True, type='str'),
                ports=dict(required=False, type='list', aliases=["port"]),
            ))

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

        # set up variables
        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_if_grp(self):
        """
        Return details about the if_group
        :param:
            name : Name of the if_group

        :return: Details about the if_group. None if not found.
        :rtype: dict
        """
        if_group_iter = netapp_utils.zapi.NaElement('net-port-get-iter')
        if_group_info = netapp_utils.zapi.NaElement('net-port-info')
        if_group_info.add_new_child('port', self.parameters['name'])
        if_group_info.add_new_child('port-type', 'if_group')
        if_group_info.add_new_child('node', self.parameters['node'])
        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(if_group_info)
        if_group_iter.add_child_elem(query)
        try:
            result = self.server.invoke_successfully(if_group_iter, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error getting if_group %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

        return_value = None

        if result.get_child_by_name('num-records') and int(
                result['num-records']) >= 1:
            if_group_attributes = result['attributes-list']['net-port-info']
            return_value = {
                'name':
                if_group_attributes['port'],
                'distribution_function':
                if_group_attributes['ifgrp-distribution-function'],
                'mode':
                if_group_attributes['ifgrp-mode'],
                'node':
                if_group_attributes['node'],
            }

        return return_value

    def get_if_grp_ports(self):
        """
        Return ports of the if_group
        :param:
            name : Name of the if_group
        :return: Ports of the if_group. None if not found.
        :rtype: dict
        """
        if_group_iter = netapp_utils.zapi.NaElement('net-port-ifgrp-get')
        if_group_iter.add_new_child('ifgrp-name', self.parameters['name'])
        if_group_iter.add_new_child('node', self.parameters['node'])
        try:
            result = self.server.invoke_successfully(if_group_iter, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error getting if_group ports %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

        port_list = []
        if result.get_child_by_name('attributes'):
            if_group_attributes = result['attributes']['net-ifgrp-info']
            if if_group_attributes.get_child_by_name('ports'):
                ports = if_group_attributes.get_child_by_name(
                    'ports').get_children()
                for each in ports:
                    port_list.append(each.get_content())
        return {'ports': port_list}

    def create_if_grp(self):
        """
        Creates a new ifgrp
        """
        route_obj = netapp_utils.zapi.NaElement("net-port-ifgrp-create")
        route_obj.add_new_child("distribution-function",
                                self.parameters['distribution_function'])
        route_obj.add_new_child("ifgrp-name", self.parameters['name'])
        route_obj.add_new_child("mode", self.parameters['mode'])
        route_obj.add_new_child("node", self.parameters['node'])
        try:
            self.server.invoke_successfully(route_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error creating if_group %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())
        for port in self.parameters.get('ports'):
            self.add_port_to_if_grp(port)

    def delete_if_grp(self):
        """
        Deletes a ifgrp
        """
        route_obj = netapp_utils.zapi.NaElement("net-port-ifgrp-destroy")
        route_obj.add_new_child("ifgrp-name", self.parameters['name'])
        route_obj.add_new_child("node", self.parameters['node'])
        try:
            self.server.invoke_successfully(route_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error deleting if_group %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def add_port_to_if_grp(self, port):
        """
        adds port to a ifgrp
        """
        route_obj = netapp_utils.zapi.NaElement("net-port-ifgrp-add-port")
        route_obj.add_new_child("ifgrp-name", self.parameters['name'])
        route_obj.add_new_child("port", port)
        route_obj.add_new_child("node", self.parameters['node'])
        try:
            self.server.invoke_successfully(route_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error adding port %s to if_group %s: %s' %
                (port, self.parameters['name'], to_native(error)),
                exception=traceback.format_exc())

    def modify_ports(self, current_ports):
        add_ports = set(self.parameters['ports']) - set(current_ports)
        remove_ports = set(current_ports) - set(self.parameters['ports'])
        for port in add_ports:
            self.add_port_to_if_grp(port)
        for port in remove_ports:
            self.remove_port_to_if_grp(port)

    def remove_port_to_if_grp(self, port):
        """
        removes port from a ifgrp
        """
        route_obj = netapp_utils.zapi.NaElement("net-port-ifgrp-remove-port")
        route_obj.add_new_child("ifgrp-name", self.parameters['name'])
        route_obj.add_new_child("port", port)
        route_obj.add_new_child("node", self.parameters['node'])
        try:
            self.server.invoke_successfully(route_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error removing port %s to if_group %s: %s' %
                (port, self.parameters['name'], 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_net_ifgrp", cserver)

    def apply(self):
        self.autosupport_log()
        current, modify = self.get_if_grp(), None
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if cd_action is None and self.parameters['state'] == 'present':
            current_ports = self.get_if_grp_ports()
            modify = self.na_helper.get_modified_attributes(
                current_ports, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_if_grp()
                elif cd_action == 'delete':
                    self.delete_if_grp()
                elif modify:
                    self.modify_ports(current_ports['ports'])
        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)
예제 #10
0
class ElementSWClusterPair(object):
    """ class to handle cluster pairing operations """
    def __init__(self):
        """
            Setup Ansible parameters and ElementSW connection
        """
        self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 dest_mvip=dict(required=True, type='str'),
                 dest_username=dict(required=False, type='str'),
                 dest_password=dict(required=False, type='str', no_log=True)))

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

        if HAS_SF_SDK is False:
            self.module.fail_json(
                msg="Unable to import the SolidFire Python SDK")
        else:
            self.elem = netapp_utils.create_sf_connection(module=self.module)

        self.elementsw_helper = NaElementSWModule(self.elem)
        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)
        # get element_sw_connection for destination cluster
        # overwrite existing source host, user and password with destination credentials
        self.module.params['hostname'] = self.parameters['dest_mvip']
        # username and password is same as source,
        # if dest_username and dest_password aren't specified
        if self.parameters.get('dest_username'):
            self.module.params['username'] = self.parameters['dest_username']
        if self.parameters.get('dest_password'):
            self.module.params['password'] = self.parameters['dest_password']
        self.dest_elem = netapp_utils.create_sf_connection(module=self.module)
        self.dest_elementsw_helper = NaElementSWModule(self.dest_elem)

    def check_if_already_paired(self, paired_clusters, hostname):
        for pair in paired_clusters.cluster_pairs:
            if pair.mvip == hostname:
                return pair.cluster_pair_id
        return None

    def get_src_pair_id(self):
        """
            Check for idempotency
        """
        # src cluster and dest cluster exist
        paired_clusters = self.elem.list_cluster_pairs()
        return self.check_if_already_paired(paired_clusters,
                                            self.parameters['dest_mvip'])

    def get_dest_pair_id(self):
        """
        Getting destination cluster_pair_id
        """
        paired_clusters = self.dest_elem.list_cluster_pairs()
        return self.check_if_already_paired(paired_clusters,
                                            self.parameters['hostname'])

    def pair_clusters(self):
        """
            Start cluster pairing on source, and complete on target cluster
        """
        try:
            pair_key = self.elem.start_cluster_pairing()
            self.dest_elem.complete_cluster_pairing(
                cluster_pairing_key=pair_key.cluster_pairing_key)
        except solidfire.common.ApiServerError as err:
            self.module.fail_json(
                msg="Error pairing cluster %s and %s" %
                (self.parameters['hostname'], self.parameters['dest_mvip']),
                exception=to_native(err))

    def unpair_clusters(self, pair_id_source, pair_id_dest):
        """
            Delete cluster pair
        """
        try:
            self.elem.remove_cluster_pair(cluster_pair_id=pair_id_source)
            self.dest_elem.remove_cluster_pair(cluster_pair_id=pair_id_dest)
        except solidfire.common.ApiServerError as err:
            self.module.fail_json(
                msg="Error unpairing cluster %s and %s" %
                (self.parameters['hostname'], self.parameters['dest_mvip']),
                exception=to_native(err))

    def apply(self):
        """
            Call create / delete cluster pair methods
        """
        pair_id_source = self.get_src_pair_id()
        # If already paired, find the cluster_pair_id of destination cluster
        if pair_id_source:
            pair_id_dest = self.get_dest_pair_id()
        # calling helper to determine action
        cd_action = self.na_helper.get_cd_action(pair_id_source,
                                                 self.parameters)
        if cd_action == "create":
            self.pair_clusters()
        elif cd_action == "delete":
            self.unpair_clusters(pair_id_source, pair_id_dest)
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapIpspace(object):
    '''Class with ipspace 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'),
            ))
        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 ipspace_get_iter(self, name):
        """
        Return net-ipspaces-get-iter query results
        :param name: Name of the ipspace
        :return: NaElement if ipspace found, None otherwise
        """
        ipspace_get_iter = netapp_utils.zapi.NaElement('net-ipspaces-get-iter')
        query_details = netapp_utils.zapi.NaElement.create_node_with_children(
            'net-ipspaces-info', **{'ipspace': name})
        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(query_details)
        ipspace_get_iter.add_child_elem(query)
        try:
            result = self.server.invoke_successfully(ipspace_get_iter,
                                                     enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as error:
            # Error 14636 denotes an ipspace does not exist
            # Error 13073 denotes an ipspace not found
            if to_native(error.code) == "14636" or to_native(
                    error.code) == "13073":
                return None
            else:
                self.module.self.fail_json(msg=to_native(error),
                                           exception=traceback.format_exc())
        return result

    def get_ipspace(self, name=None):
        """
        Fetch details if ipspace exists
        :param name: Name of the ipspace to be fetched
        :return:
            Dictionary of current details if ipspace found
            None if ipspace is not found
        """
        if name is None:
            name = self.parameters['name']
        ipspace_get = self.ipspace_get_iter(name)
        if (ipspace_get and ipspace_get.get_child_by_name('num-records')
                and int(ipspace_get.get_child_content('num-records')) >= 1):
            current_ipspace = dict()
            attr_list = ipspace_get.get_child_by_name('attributes-list')
            attr = attr_list.get_child_by_name('net-ipspaces-info')
            current_ipspace['name'] = attr.get_child_content('ipspace')
            return current_ipspace
        return None

    def create_ipspace(self):
        """
        Create ipspace
        :return: None
        """
        ipspace_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'net-ipspaces-create', **{'ipspace': self.parameters['name']})
        try:
            self.server.invoke_successfully(ipspace_create,
                                            enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as error:
            self.module.self.fail_json(
                msg="Error provisioning ipspace %s: %s" %
                (self.parameters['name'], to_native(error)),
                exception=traceback.format_exc())

    def delete_ipspace(self):
        """
        Destroy ipspace
        :return: None
        """
        ipspace_destroy = netapp_utils.zapi.NaElement.create_node_with_children(
            'net-ipspaces-destroy', **{'ipspace': self.parameters['name']})
        try:
            self.server.invoke_successfully(ipspace_destroy,
                                            enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as error:
            self.module.self.fail_json(
                msg="Error removing ipspace %s: %s" %
                (self.parameters['name'], to_native(error)),
                exception=traceback.format_exc())

    def rename_ipspace(self):
        """
        Rename an ipspace
        :return: Nothing
        """
        ipspace_rename = netapp_utils.zapi.NaElement.create_node_with_children(
            'net-ipspaces-rename', **{
                'ipspace': self.parameters['from_name'],
                'new-name': self.parameters['name']
            })
        try:
            self.server.invoke_successfully(ipspace_rename,
                                            enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg="Error renaming ipspace %s: %s" %
                (self.parameters['from_name'], to_native(error)),
                exception=traceback.format_exc())

    def apply(self):
        """
        Apply action to the ipspace
        :return: Nothing
        """
        current = self.get_ipspace()
        # rename and create are mutually exclusive
        rename, cd_action = None, None
        if self.parameters.get('from_name'):
            rename = self.na_helper.is_rename_action(
                self.get_ipspace(self.parameters['from_name']), current)
            if rename is None:
                self.module.fail_json(
                    msg="Error renaming: ipspace %s does not exist" %
                    self.parameters['from_name'])
        else:
            cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if rename:
                    self.rename_ipspace()
                elif cd_action == 'create':
                    self.create_ipspace()
                elif cd_action == 'delete':
                    self.delete_ipspace()
        self.module.exit_json(changed=self.na_helper.changed)
예제 #12
0
class NetAppOntapIgroupInitiator(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'),
            names=dict(required=True, type='list', aliases=['name']),
            initiator_group=dict(required=True, 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_initiators(self):
        """
        Get the existing list of initiators from an igroup
        :rtype: list() or None
        """
        igroup_info = netapp_utils.zapi.NaElement('igroup-get-iter')
        attributes = dict(query={'initiator-group-info': {'initiator-group-name': self.parameters['initiator_group'],
                                                          'vserver': self.parameters['vserver']}})
        igroup_info.translate_struct(attributes)
        result, current = None, []

        try:
            result = self.server.invoke_successfully(igroup_info, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error fetching igroup info %s: %s' % (self.parameters['initiator_group'],
                                                                             to_native(error)),
                                  exception=traceback.format_exc())

        if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1:
            igroup_info = result.get_child_by_name('attributes-list').get_child_by_name('initiator-group-info')
            if igroup_info.get_child_by_name('initiators') is not None:
                current = [initiator['initiator-name'] for initiator in igroup_info['initiators'].get_children()]
        return current

    def modify_initiator(self, initiator_name, zapi):
        """
        Add or remove an initiator to/from an igroup
        """
        options = {'initiator-group-name': self.parameters['initiator_group'],
                   'initiator': initiator_name}
        initiator_modify = netapp_utils.zapi.NaElement.create_node_with_children(zapi, **options)

        try:
            self.server.invoke_successfully(initiator_modify, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error modifying igroup initiator %s: %s' % (initiator_name,
                                                                                   to_native(error)),
                                  exception=traceback.format_exc())

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

    def apply(self):
        self.autosupport_log()
        initiators = self.get_initiators()
        for initiator in self.parameters['names']:
            present = None
            if initiator in initiators:
                present = True
            cd_action = self.na_helper.get_cd_action(present, self.parameters)
            if self.na_helper.changed:
                if self.module.check_mode:
                    pass
                else:
                    if cd_action == 'create':
                        self.modify_initiator(initiator, 'igroup-add')
                    elif cd_action == 'delete':
                        self.modify_initiator(initiator, 'igroup-remove')
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppONTAPCluster(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, choices=['present'], default='present'),
            cluster_name=dict(required=False, type='str'),
            cluster_ip_address=dict(required=False, type='str'),
            license_code=dict(required=False, type='str'),
            license_package=dict(required=False, type='str'),
            node_serial_number=dict(required=False, type='str')
        ))

        self.module = AnsibleModule(
            argument_spec=self.argument_spec,
            supports_check_mode=True,
            required_together=[
                ['license_package', 'node_serial_number']
            ]
        )

        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_licensing_status(self):
        """
            Check licensing status

            :return: package (key) and licensing status (value)
            :rtype: dict
        """
        license_status = netapp_utils.zapi.NaElement(
            'license-v2-status-list-info')
        try:
            result = self.server.invoke_successfully(license_status,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error checking license status: %s" %
                                  to_native(error), exception=traceback.format_exc())

        return_dictionary = {}
        license_v2_status = result.get_child_by_name('license-v2-status')
        if license_v2_status:
            for license_v2_status_info in license_v2_status.get_children():
                package = license_v2_status_info.get_child_content('package')
                status = license_v2_status_info.get_child_content('method')
                return_dictionary[package] = status

        return return_dictionary

    def create_cluster(self):
        """
        Create a cluster
        """
        cluster_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'cluster-create', **{'cluster-name': self.parameters['cluster_name']})

        try:
            self.server.invoke_successfully(cluster_create,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            # Error 36503 denotes node already being used.
            if to_native(error.code) == "36503":
                return False
            else:
                self.module.fail_json(msg='Error creating cluster %s: %s'
                                      % (self.parameters['cluster_name'], to_native(error)),
                                      exception=traceback.format_exc())
        return True

    def cluster_join(self):
        """
        Add a node to an existing cluster
        """
        if self.parameters.get('cluster_ip_address') is not None:
            cluster_add_node = netapp_utils.zapi.NaElement.create_node_with_children(
                'cluster-join', **{'cluster-ip-address': self.parameters['cluster_ip_address']})
            for_fail_attribute = self.parameters.get('cluster_ip_address')
        elif self.parameters.get('cluster_name') is not None:
            cluster_add_node = netapp_utils.zapi.NaElement.create_node_with_children(
                'cluster-join', **{'cluster-name': self.parameters['cluster_name']})
            for_fail_attribute = self.parameters.get('cluster_name')
        else:
            return False
        try:
            self.server.invoke_successfully(cluster_add_node, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            # Error 36503 denotes node already being used.
            if to_native(error.code) == "36503":
                return False
            else:
                self.module.fail_json(msg='Error adding node to cluster %s: %s'
                                      % (for_fail_attribute, to_native(error)),
                                      exception=traceback.format_exc())
        return True

    def license_v2_add(self):
        """
        Apply a license to cluster
        """
        license_add = netapp_utils.zapi.NaElement.create_node_with_children('license-v2-add')
        license_add.add_node_with_children('codes', **{'license-code-v2': self.parameters['license_code']})
        try:
            self.server.invoke_successfully(license_add, enable_tunneling=True)

        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error adding license %s: %s'
                                  % (self.parameters['license_code'], to_native(error)),
                                  exception=traceback.format_exc())

    def license_v2_delete(self):
        """
        Delete license from cluster
        """
        license_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'license-v2-delete', **{'package': self.parameters['license_package'],
                                    'serial-number': self.parameters['node_serial_number']})
        try:
            self.server.invoke_successfully(license_delete, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error deleting license : %s' % (to_native(error)),
                                  exception=traceback.format_exc())

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

    def apply(self):
        """
        Apply action to cluster
        """
        property_changed = False
        create_flag = False
        join_flag = False

        self.autosupport_log()
        license_status = self.get_licensing_status()

        if self.module.check_mode:
            pass
        else:
            if self.parameters.get('state') == 'present':
                if self.parameters.get('cluster_name') is not None:
                    create_flag = self.create_cluster()
                if not create_flag:
                    join_flag = self.cluster_join()
                if self.parameters.get('license_code') is not None:
                    self.license_v2_add()
                    property_changed = True
                if self.parameters.get('license_package') is not None and\
                        self.parameters.get('node_serial_number') is not None:
                    if license_status.get(str(self.parameters.get('license_package')).lower()) != 'none':
                        self.license_v2_delete()
                        property_changed = True
                if property_changed:
                    new_license_status = self.get_licensing_status()
                    if local_cmp(license_status, new_license_status) == 0:
                        property_changed = False
        changed = property_changed or create_flag or join_flag
        self.module.exit_json(changed=changed)
예제 #14
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)
class NetAppONTAPVolumeClone(object):
    """
        Creates a volume clone
    """

    def __init__(self):
        """
            Initialize the NetAppOntapVolumeClone class
        """
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(dict(
            state=dict(required=False, choices=['present'], default='present'),
            parent_volume=dict(required=True, type='str'),
            name=dict(required=True, type='str', aliases=["volume"]),
            vserver=dict(required=True, type='str'),
            parent_snapshot=dict(required=False, type='str', default=None),
            parent_vserver=dict(required=False, type='str', default=None),
            qos_policy_group_name=dict(required=False, type='str', default=None),
            space_reserve=dict(required=False, choices=['volume', 'none'], default=None),
            volume_type=dict(required=False, choices=['rw', 'dp']),
            junction_path=dict(required=False, type='str', default=None),
            uid=dict(required=False, type='int'),
            gid=dict(required=False, type='int')
        ))

        self.module = AnsibleModule(
            argument_spec=self.argument_spec,
            supports_check_mode=True,
            required_together=[
                ['uid', 'gid']
            ]
        )

        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 create_volume_clone(self):
        """
        Creates a new volume clone
        """
        clone_obj = netapp_utils.zapi.NaElement('volume-clone-create')
        clone_obj.add_new_child("parent-volume", self.parameters['parent_volume'])
        clone_obj.add_new_child("volume", self.parameters['volume'])
        if self.parameters.get('qos_policy_group_name'):
            clone_obj.add_new_child("qos-policy-group-name", self.parameters['qos_policy_group_name'])
        if self.parameters.get('space_reserve'):
            clone_obj.add_new_child("space-reserve", self.parameters['space_reserve'])
        if self.parameters.get('parent_snapshot'):
            clone_obj.add_new_child("parent-snapshot", self.parameters['parent_snapshot'])
        if self.parameters.get('parent_vserver'):
            clone_obj.add_new_child("parent-vserver", self.parameters['parent_vserver'])
        if self.parameters.get('volume_type'):
            clone_obj.add_new_child("volume-type", self.parameters['volume_type'])
        if self.parameters.get('junction_path'):
            clone_obj.add_new_child("junction-path", self.parameters['junction_path'])
        if self.parameters.get('uid'):
            clone_obj.add_new_child("uid", str(self.parameters['uid']))
            clone_obj.add_new_child("gid", str(self.parameters['gid']))
        try:
            self.server.invoke_successfully(clone_obj, True)
        except netapp_utils.zapi.NaApiError as exc:
            self.module.fail_json(msg='Error creating volume clone: %s: %s' %
                                      (self.parameters['volume'], to_native(exc)), exception=traceback.format_exc())

    def get_volume_clone(self):
        clone_obj = netapp_utils.zapi.NaElement('volume-clone-get')
        clone_obj.add_new_child("volume", self.parameters['volume'])
        try:
            results = self.server.invoke_successfully(clone_obj, True)
            if results.get_child_by_name('attributes'):
                attributes = results.get_child_by_name('attributes')
                info = attributes.get_child_by_name('volume-clone-info')
                parent_volume = info.get_child_content('parent-volume')
                # checking if clone volume name already used to create by same parent volume
                if parent_volume == self.parameters['parent_volume']:
                    return results
        except netapp_utils.zapi.NaApiError as error:
            # Error 15661 denotes an volume clone not being found.
            if to_native(error.code) == "15661":
                pass
            else:
                self.module.fail_json(msg='Error fetching volume clone information %s: %s' %
                                          (self.parameters['volume'], to_native(error)), exception=traceback.format_exc())
        return None

    def apply(self):
        """
        Run Module based on play book
        """
        netapp_utils.ems_log_event("na_ontap_volume_clone", self.server)
        current = self.get_volume_clone()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_volume_clone()
        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)
class AwsCvsNetappSnapshot(object):
    """
    Contains methods to parse arguments,
    derive details of AWS_CVS objects
    and send requests to AWS CVS via
    the restApi
    """
    def __init__(self):
        """
        Parse arguments, setup state variables,
        check parameters and ensure request module is installed
        """
        self.argument_spec = netapp_utils.aws_cvs_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=True, choices=['present', 'absent']),
                 region=dict(required=True, type='str'),
                 name=dict(required=True, type='str'),
                 from_name=dict(required=False, type='str'),
                 fileSystemId=dict(required=False, type='str')))

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

        self.na_helper = NetAppModule()

        # set up state variables
        self.parameters = self.na_helper.set_parameters(self.module.params)
        # Calling generic AWSCVS restApi class
        self.restApi = AwsCvsRestAPI(self.module)

        # Checking for the parameters passed and create new parameters list
        self.data = {}
        for key in self.parameters.keys():
            self.data[key] = self.parameters[key]

    def getSnapshotId(self, name):
        # Check if  snapshot exists
        # Return snapshot Id  If Snapshot is found, None otherwise
        list_snapshots, error = self.restApi.get('Snapshots')

        if error:
            self.module.fail_json(msg=error)

        for snapshot in list_snapshots:
            if snapshot['name'] == name:
                return snapshot['snapshotId']
        return None

    def getfilesystemId(self):
        # Check given FileSystem is exists
        # Return fileSystemId is found, None otherwise
        list_filesystem, error = self.restApi.get('FileSystems')

        if error:
            self.module.fail_json(msg=error)
        for FileSystem in list_filesystem:
            if FileSystem['fileSystemId'] == self.parameters['fileSystemId']:
                return FileSystem['fileSystemId']
            elif FileSystem['creationToken'] == self.parameters[
                    'fileSystemId']:
                return FileSystem['fileSystemId']
        return None

    def create_snapshot(self):
        # Create Snapshot
        api = 'Snapshots'
        response, error = self.restApi.post(api, self.data)
        if error:
            self.module.fail_json(msg=error)

    def rename_snapshot(self, snapshotId):
        # Rename Snapshot
        api = 'Snapshots/' + snapshotId
        response, error = self.restApi.put(api, self.data)
        if error:
            self.module.fail_json(msg=error)

    def delete_snapshot(self, snapshotId):
        # Delete Snapshot
        api = 'Snapshots/' + snapshotId
        data = None
        response, error = self.restApi.delete(api, self.data)
        if error:
            self.module.fail_json(msg=error)

    def apply(self):
        """
        Perform pre-checks, call functions and exit
        """
        self.snapshotId = self.getSnapshotId(self.data['name'])

        if self.snapshotId is None and 'fileSystemId' in self.data:
            self.fileSystemId = self.getfilesystemId()
            self.data['fileSystemId'] = self.fileSystemId
            if self.fileSystemId is None:
                self.module.fail_json(
                    msg='Error: Specified filesystem id %s does not exist ' %
                    self.data['fileSystemId'])

        cd_action = self.na_helper.get_cd_action(self.snapshotId, self.data)
        result_message = ""
        if self.na_helper.changed:
            if self.module.check_mode:
                # Skip changes
                result_message = "Check mode, skipping changes"
            else:
                if cd_action == "delete":
                    self.delete_snapshot(self.snapshotId)
                    result_message = "Snapshot Deleted"

                elif cd_action == "create":
                    if 'from_name' in self.data:
                        # If cd_action is create and from_name is given
                        snapshotId = self.getSnapshotId(self.data['from_name'])
                        if snapshotId is not None:
                            # If resource pointed by from_name exists, rename the snapshot to name
                            self.rename_snapshot(snapshotId)
                            result_message = "Snapshot Updated"
                        else:
                            # If resource pointed by from_name does not exists, error out
                            self.module.fail_json(
                                msg="Resource does not exist : %s" %
                                self.data['from_name'])
                    else:
                        self.create_snapshot()
                        # If from_name is not defined, Create from scratch.
                        result_message = "Snapshot Created"

        self.module.exit_json(changed=self.na_helper.changed,
                              msg=result_message)
예제 #18
0
class NetAppOntapDns(object):
    """
    Enable and Disable dns
    """

    def __init__(self):
        self.use_rest = False
        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'),
            domains=dict(required=False, type='list'),
            nameservers=dict(required=False, type='list'),
            skip_validation=dict(required=False, type='bool')
        ))

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

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

        # REST API should be used for ONTAP 9.6 or higher, ZAPI for lower version
        self.restApi = OntapRestAPI(self.module)
        # some attributes are not supported in earlier REST implementation
        unsupported_rest_properties = ['skip_validation']
        used_unsupported_rest_properties = [x for x in unsupported_rest_properties if x in self.parameters]
        self.use_rest, error = self.restApi.is_rest(used_unsupported_rest_properties)

        if error is not None:
            self.module.fail_json(msg=error)

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

    def create_dns(self):
        """
        Create DNS server
        :return: none
        """
        if self.use_rest:
            api = 'name-services/dns'
            params = {}
            params['domains'] = self.parameters['domains']
            params['servers'] = self.parameters['nameservers']
            params['svm'] = {'name': self.parameters['vserver']}
            message, error = self.restApi.post(api, params)
            if error:
                self.module.fail_json(msg=error)
        else:
            dns = netapp_utils.zapi.NaElement('net-dns-create')
            nameservers = netapp_utils.zapi.NaElement('name-servers')
            domains = netapp_utils.zapi.NaElement('domains')
            for each in self.parameters['nameservers']:
                ip_address = netapp_utils.zapi.NaElement('ip-address')
                ip_address.set_content(each)
                nameservers.add_child_elem(ip_address)
            dns.add_child_elem(nameservers)
            for each in self.parameters['domains']:
                domain = netapp_utils.zapi.NaElement('string')
                domain.set_content(each)
                domains.add_child_elem(domain)
            dns.add_child_elem(domains)
            if self.parameters.get('skip_validation'):
                validation = netapp_utils.zapi.NaElement('skip-config-validation')
                validation.set_content(str(self.parameters['skip_validation']))
                dns.add_child_elem(validation)
            try:
                self.server.invoke_successfully(dns, True)
            except netapp_utils.zapi.NaApiError as error:
                self.module.fail_json(msg='Error creating dns: %s' %
                                          (to_native(error)),
                                      exception=traceback.format_exc())

    def destroy_dns(self, dns_attrs):
        """
        Destroys an already created dns
        :return:
        """
        if self.use_rest:
            uuid = dns_attrs['records'][0]['svm']['uuid']
            api = 'name-services/dns/' + uuid
            data = None
            message, error = self.restApi.delete(api, data)
            if error:
                self.module.fail_json(msg=error)
        else:
            try:
                self.server.invoke_successfully(netapp_utils.zapi.NaElement('net-dns-destroy'), True)
            except netapp_utils.zapi.NaApiError as error:
                self.module.fail_json(msg='Error destroying dns %s' %
                                          (to_native(error)),
                                      exception=traceback.format_exc())

    def get_dns(self):
        if self.use_rest:
            api = "name-services/dns"
            params = {'fields': 'domains,servers,svm',
                      "svm.name": self.parameters['vserver']}
            message, error = self.restApi.get(api, params)
            if error:
                self.module.fail_json(msg=error)
            if len(message.keys()) == 0:
                message = None
            elif 'records' in message and len(message['records']) == 0:
                message = None
            elif 'records' not in message or len(message['records']) != 1:
                error = "Unexpected response from %s: %s" % (api, repr(message))
                self.module.fail_json(msg=error)
            return message
        else:
            dns_obj = netapp_utils.zapi.NaElement('net-dns-get')
            try:
                result = self.server.invoke_successfully(dns_obj, True)
            except netapp_utils.zapi.NaApiError as error:
                if to_native(error.code) == "15661":
                    # 15661 is object not found
                    return None
                else:
                    self.module.fail_json(msg=to_native(
                        error), exception=traceback.format_exc())

            # read data for modify
            attrs = dict()
            attributes = result.get_child_by_name('attributes')
            dns_info = attributes.get_child_by_name('net-dns-info')
            nameservers = dns_info.get_child_by_name('name-servers')
            attrs['nameservers'] = [each.get_content() for each in nameservers.get_children()]
            domains = dns_info.get_child_by_name('domains')
            attrs['domains'] = [each.get_content() for each in domains.get_children()]
            attrs['skip_validation'] = dns_info.get_child_by_name('skip-config-validation')
            return attrs

    def modify_dns(self, dns_attrs):
        if self.use_rest:
            changed = False
            params = {}
            if dns_attrs['records'][0]['servers'] != self.parameters['nameservers']:
                changed = True
                params['servers'] = self.parameters['nameservers']
            if dns_attrs['records'][0]['domains'] != self.parameters['domains']:
                changed = True
                params['domains'] = self.parameters['domains']
            if changed:
                uuid = dns_attrs['records'][0]['svm']['uuid']
                api = "name-services/dns/" + uuid
                message, error = self.restApi.patch(api, params)
                if error:
                    self.module.fail_json(msg=error)

        else:
            changed = False
            dns = netapp_utils.zapi.NaElement('net-dns-modify')
            if dns_attrs['nameservers'] != self.parameters['nameservers']:
                changed = True
                nameservers = netapp_utils.zapi.NaElement('name-servers')
                for each in self.parameters['nameservers']:
                    ip_address = netapp_utils.zapi.NaElement('ip-address')
                    ip_address.set_content(each)
                    nameservers.add_child_elem(ip_address)
                dns.add_child_elem(nameservers)
            if dns_attrs['domains'] != self.parameters['domains']:
                changed = True
                domains = netapp_utils.zapi.NaElement('domains')
                for each in self.parameters['domains']:
                    domain = netapp_utils.zapi.NaElement('string')
                    domain.set_content(each)
                    domains.add_child_elem(domain)
                dns.add_child_elem(domains)
            if changed:
                if self.parameters.get('skip_validation'):
                    validation = netapp_utils.zapi.NaElement('skip-config-validation')
                    validation.set_content(str(self.parameters['skip_validation']))
                    dns.add_child_elem(validation)
                try:
                    self.server.invoke_successfully(dns, True)
                except netapp_utils.zapi.NaApiError as error:
                    self.module.fail_json(msg='Error modifying dns %s' %
                                              (to_native(error)), exception=traceback.format_exc())
        return changed

    def apply(self):
        # asup logging
        if not self.use_rest:
            netapp_utils.ems_log_event("na_ontap_dns", self.server)
        dns_attrs = self.get_dns()
        changed = False
        if self.parameters['state'] == 'present':
            if dns_attrs is not None:
                changed = self.modify_dns(dns_attrs)
            else:
                self.create_dns()
                changed = True
        else:
            if dns_attrs is not None:
                self.destroy_dns(dns_attrs)
                changed = True
        self.module.exit_json(changed=changed)
class NetAppONTAPSoftwareUpdate(object):
    """
    Class with ONTAP software update methods
    """
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            type='str',
                            choices=['present', 'absent'],
                            default='present'),
                 nodes=dict(required=False, type='list', aliases=["node"]),
                 package_version=dict(required=True, type='str'),
                 package_url=dict(required=True, type='str'),
                 ignore_validation_warning=dict(required=False,
                                                type='bool',
                                                default=False)))

        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 cluster_image_get_iter(self):
        """
        Compose NaElement object to query current version
        :return: NaElement object for cluster-image-get-iter with query
        """
        cluster_image_get = netapp_utils.zapi.NaElement(
            'cluster-image-get-iter')
        query = netapp_utils.zapi.NaElement('query')
        cluster_image_info = netapp_utils.zapi.NaElement('cluster-image-info')
        query.add_child_elem(cluster_image_info)
        cluster_image_get.add_child_elem(query)
        return cluster_image_get

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

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

    def cluster_image_update_progress_get(self):
        """
        Get current cluster image update progress info
        :return: Dictionary of cluster image update progress if query successful, else return None
        """
        cluster_update_progress_get = netapp_utils.zapi.NaElement(
            'cluster-image-update-progress-info')
        cluster_update_progress_info = dict()
        try:
            result = self.server.invoke_successfully(
                cluster_update_progress_get, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            # return empty dict on error to satisfy package delete upon image update
            if to_native(
                    error.code
            ) == 'Unexpected error' and self.parameters.get('https') is True:
                return cluster_update_progress_info
            else:
                self.module.fail_json(
                    msg=
                    'Error fetching cluster image update progress details: %s'
                    % (to_native(error)),
                    exception=traceback.format_exc())
        # return cluster image update progress details
        if result.get_child_by_name('attributes').get_child_by_name(
                'ndu-progress-info'):
            update_progress_info = result.get_child_by_name(
                'attributes').get_child_by_name('ndu-progress-info')
            cluster_update_progress_info[
                'overall_status'] = update_progress_info.get_child_content(
                    'overall-status')
            cluster_update_progress_info['completed_node_count'] = update_progress_info.\
                get_child_content('completed-node-count')
        return cluster_update_progress_info

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

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

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

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

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

    def apply(self):
        """
        Apply action to update ONTAP software
        """
        if self.parameters.get('https') is not True:
            self.module.fail_json(msg='https parameter must be True')
        changed = False
        self.autosupport_log()
        current = self.cluster_image_get()
        if self.parameters.get('nodes'):
            for node in self.parameters['nodes']:
                self.cluster_image_get_for_node(node)
        if self.parameters.get('state') == 'present' and current:
            package_exists = self.cluster_image_package_download()
            if package_exists is False:
                cluster_download_progress = self.cluster_image_package_download_progress(
                )
                while cluster_download_progress.get(
                        'progress_status') == 'async_pkg_get_phase_running':
                    time.sleep(5)
                    cluster_download_progress = self.cluster_image_package_download_progress(
                    )
                if cluster_download_progress.get(
                        'progress_status') == 'async_pkg_get_phase_complete':
                    self.cluster_image_update()
                    changed = True
                else:
                    self.module.fail_json(
                        msg='Error downloading package: %s' %
                        (cluster_download_progress['failure_reason']))
            else:
                self.cluster_image_update()
                changed = True
            # delete package once update is completed
            cluster_update_progress = self.cluster_image_update_progress_get()
            while not cluster_update_progress or cluster_update_progress.get(
                    'overall_status') == 'in_progress':
                time.sleep(25)
                cluster_update_progress = self.cluster_image_update_progress_get(
                )
            if cluster_update_progress.get('overall_status') == 'completed':
                self.cluster_image_package_delete()
        self.module.exit_json(changed=changed)
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 NetAppONTAPPortset(object):
    """
    Methods to create or delete portset
    """
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False, default='present'),
                 vserver=dict(required=True, type='str'),
                 name=dict(required=True, type='str'),
                 type=dict(required=False,
                           type='str',
                           choices=['fcp', 'iscsi', 'mixed']),
                 force=dict(required=False, type='bool', default=False),
                 ports=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)

        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 portset_get_iter(self):
        """
        Compose NaElement object to query current portset using vserver, portset-name and portset-type parameters
        :return: NaElement object for portset-get-iter with query
        """
        portset_get = netapp_utils.zapi.NaElement('portset-get-iter')
        query = netapp_utils.zapi.NaElement('query')
        portset_info = netapp_utils.zapi.NaElement('portset-info')
        portset_info.add_new_child('vserver', self.parameters['vserver'])
        portset_info.add_new_child('portset-name', self.parameters['name'])
        if self.parameters.get('type'):
            portset_info.add_new_child('portset-type', self.parameters['type'])
        query.add_child_elem(portset_info)
        portset_get.add_child_elem(query)
        return portset_get

    def portset_get(self):
        """
        Get current portset info
        :return: Dictionary of current portset details if query successful, else return None
        """
        portset_get_iter = self.portset_get_iter()
        result, portset_info = None, dict()
        try:
            result = self.server.invoke_successfully(portset_get_iter,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error fetching portset %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())
        # return portset details
        if result.get_child_by_name('num-records') and int(
                result.get_child_content('num-records')) > 0:
            portset_get_info = result.get_child_by_name(
                'attributes-list').get_child_by_name('portset-info')
            if int(portset_get_info.get_child_content(
                    'portset-port-total')) > 0:
                ports = portset_get_info.get_child_by_name('portset-port-info')
                portset_info['ports'] = [
                    port.get_content() for port in ports.get_children()
                ]
            else:
                portset_info['ports'] = []
            return portset_info
        return None

    def create_portset(self):
        """
        Create a portset
        """
        if self.parameters.get('type') is None:
            self.module.fail_json(
                msg='Error: Missing required parameter for create (type)')
        portset_info = netapp_utils.zapi.NaElement("portset-create")
        portset_info.add_new_child("portset-name", self.parameters['name'])
        portset_info.add_new_child("portset-type", self.parameters['type'])
        try:
            self.server.invoke_successfully(portset_info,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error creating portset %s: %s" %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def delete_portset(self):
        """
        Delete a portset
        """
        portset_info = netapp_utils.zapi.NaElement("portset-destroy")
        portset_info.add_new_child("portset-name", self.parameters['name'])
        if self.parameters.get('force'):
            portset_info.add_new_child("force", str(self.parameters['force']))
        try:
            self.server.invoke_successfully(portset_info,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error deleting portset %s: %s" %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def remove_ports(self, ports):
        """
        Removes all existing ports from portset
        :return: None
        """
        for port in ports:
            self.modify_port(port, 'portset-remove', 'removing')

    def add_ports(self):
        """
        Add the list of ports to portset
        :return: None
        """
        # don't add if ports is empty string
        if self.parameters.get('ports') == [
                ''
        ] or self.parameters.get('ports') is None:
            return
        for port in self.parameters['ports']:
            self.modify_port(port, 'portset-add', 'adding')

    def modify_port(self, port, zapi, action):
        """
        Add or remove an port to/from a portset
        """
        port.strip(
        )  # remove leading spaces if any (eg: if user types a space after comma in initiators list)
        options = {
            'portset-name': self.parameters['name'],
            'portset-port-name': port
        }

        portset_modify = netapp_utils.zapi.NaElement.create_node_with_children(
            zapi, **options)

        try:
            self.server.invoke_successfully(portset_modify,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error %s port in portset %s: %s' %
                (action, self.parameters['name'], to_native(error)),
                exception=traceback.format_exc())

    def apply(self):
        """
        Applies action from playbook
        """
        netapp_utils.ems_log_event("na_ontap_autosupport", self.server)
        current, modify = self.portset_get(), None
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if cd_action is None and self.parameters['state'] == 'present':
            modify = self.na_helper.get_modified_attributes(
                current, self.parameters)

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_portset()
                    self.add_ports()
                elif cd_action == 'delete':
                    self.delete_portset()
                elif modify:
                    self.remove_ports(current['ports'])
                    self.add_ports()
        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 NetAppOntapLDAPClient(object):
    '''
    LDAP Client definition class
    '''
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(base_dn=dict(required=False, type='str'),
                 base_scope=dict(required=False,
                                 default=None,
                                 choices=['subtree', 'onelevel', 'base']),
                 bind_dn=dict(required=False, default=None, type='str'),
                 bind_password=dict(type='str',
                                    required=False,
                                    default=None,
                                    no_log=True),
                 name=dict(required=True, type='str'),
                 ldap_servers=dict(required_if=[["state", "present"]],
                                   type='list'),
                 min_bind_level=dict(required=False,
                                     default=None,
                                     choices=['anonymous', 'simple', 'sasl']),
                 port=dict(required=False, default=None, type='int'),
                 query_timeout=dict(required=False, default=None, type='int'),
                 referral_enabled=dict(required=False,
                                       default=None,
                                       choices=['true', 'false']),
                 schema=dict(
                     required_if=[["state", "present"]],
                     default=None,
                     type='str',
                     choices=['AD-IDMU', 'AD-SFU', 'MS-AD-BIS', 'RFC-2307']),
                 session_security=dict(required=False,
                                       default=None,
                                       choices=['true', 'false']),
                 state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 use_start_tls=dict(required=False,
                                    default=None,
                                    choices=['true', 'false']),
                 vserver=dict(required=True, type='str')))

        self.module = AnsibleModule(
            argument_spec=self.argument_spec,
            supports_check_mode=True,
            required_if=[('state', 'present', ['ldap_servers', 'schema'])],
        )
        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 = [
            'base_dn', 'base_scope', 'bind_dn', 'bind_password',
            'min_bind_level', 'port', 'query_timeout', 'referral_enabled',
            'session_security', 'use_start_tls'
        ]

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

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

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

        if vserver_name is None:
            vserver_name = '*'

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

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

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

        # Get LDAP client configuration details
        client_config_details = None
        if (result.get_child_by_name('num-records')
                and int(result.get_child_content('num-records')) >= 1):
            attributes_list = result.get_child_by_name('attributes-list')
            client_config_info = attributes_list.get_child_by_name(
                'ldap-client')

            # Get LDAP servers list
            ldap_server_list = list()
            get_list = client_config_info.get_child_by_name('ldap-servers')
            if get_list is not None:
                ldap_servers = get_list.get_children()
                for ldap_server in ldap_servers:
                    ldap_server_list.append(ldap_server.get_content())

            # Define config details structure
            client_config_details = {
                'name':
                client_config_info.get_child_content('ldap-client-config'),
                'ldap_servers':
                client_config_info.get_child_content('ldap-servers'),
                'base_dn':
                client_config_info.get_child_content('base-dn'),
                'base_scope':
                client_config_info.get_child_content('base-scope'),
                'bind_dn':
                client_config_info.get_child_content('bind-dn'),
                'bind_password':
                client_config_info.get_child_content('bind-password'),
                'min_bind_level':
                client_config_info.get_child_content('min-bind-level'),
                'port':
                client_config_info.get_child_content('port'),
                'query_timeout':
                client_config_info.get_child_content('query-timeout'),
                'referral_enabled':
                client_config_info.get_child_content('referral-enabled'),
                'schema':
                client_config_info.get_child_content('schema'),
                'session_security':
                client_config_info.get_child_content('session-security'),
                'use_start_tls':
                client_config_info.get_child_content('use-start-tls'),
                'vserver':
                client_config_info.get_child_content('vserver')
            }

        return client_config_details

    def create_ldap_client(self):
        '''
        Create LDAP client configuration
        '''
        # LDAP servers NaElement
        ldap_servers_element = netapp_utils.zapi.NaElement('ldap-servers')

        # Mandatory options
        for ldap_server_name in self.parameters['ldap_servers']:
            ldap_servers_element.add_new_child('string', ldap_server_name)

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

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

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

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

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

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

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

        for attribute in modify:
            # LDAP_servers
            if attribute == 'ldap_servers':
                ldap_servers_element = netapp_utils.zapi.NaElement(
                    'ldap-servers')
                for ldap_server_name in self.parameters['ldap_servers']:
                    ldap_servers_element.add_new_child('string',
                                                       ldap_server_name)
                ldap_client_modify.add_child_elem(ldap_servers_element)

            # Simple attributes
            if attribute in self.simple_attributes:
                ldap_client_modify.add_new_child(
                    str(attribute).replace('_', '-'),
                    self.parameters[attribute])

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

    def apply(self):
        '''Call create/modify/delete operations.'''
        current = self.get_ldap_client()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        modify = self.na_helper.get_modified_attributes(
            current, self.parameters)
        # create an ems log event for users with auto support turned on
        netapp_utils.ems_log_event("na_ontap_ldap_client", self.server)

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_ldap_client()
                elif cd_action == 'delete':
                    self.delete_ldap_client()
                elif modify:
                    self.modify_ldap_client(modify)
        self.module.exit_json(changed=self.na_helper.changed)
class AwsCvsNetappFileSystem(object):
    """
    Contains methods to parse arguments,
    derive details of AWS_CVS objects
    and send requests to AWS CVS via
    the restApi
    """
    def __init__(self):
        """
        Parse arguments, setup state variables,
        check parameters and ensure request module is installed
        """
        self.argument_spec = netapp_utils.aws_cvs_host_argument_spec()
        self.argument_spec.update(
            dict(
                state=dict(required=True, choices=['present', 'absent']),
                region=dict(required=True, type='str'),
                creationToken=dict(required=True, type='str'),
                quotaInBytes=dict(required=False, type='int'),
                serviceLevel=dict(required=False,
                                  choices=['standard', 'premium', 'extreme']),
                exportPolicy=dict(
                    type='dict',
                    options=dict(rules=dict(
                        type='list',
                        options=dict(
                            allowedClients=dict(required=False, type='str'),
                            cifs=dict(required=False, type='bool'),
                            nfsv3=dict(required=False, type='bool'),
                            nfsv4=dict(required=False, type='bool'),
                            ruleIndex=dict(required=False, type='int'),
                            unixReadOnly=dict(required=False, type='bool'),
                            unixReadWrite=dict(required=False,
                                               type='bool'))))),
            ))

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

        self.na_helper = NetAppModule()

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

        # Calling generic AWSCVS restApi class
        self.restApi = AwsCvsRestAPI(self.module)

        self.data = {}
        for key in self.parameters.keys():
            self.data[key] = self.parameters[key]

    def get_filesystemId(self):
        # Check given FileSystem is exists
        # Return fileSystemId is found, None otherwise
        list_filesystem, error = self.restApi.get('FileSystems')
        if error:
            self.module.fail_json(msg=error)

        for FileSystem in list_filesystem:
            if FileSystem['creationToken'] == self.parameters['creationToken']:
                return FileSystem['fileSystemId']
        return None

    def get_filesystem(self, fileSystemId):
        # Get FileSystem information by fileSystemId
        # Return fileSystem Information
        filesystemInfo, error = self.restApi.get('FileSystems/%s' %
                                                 fileSystemId)
        if error:
            self.module.fail_json(msg=error)
        else:
            return filesystemInfo
        return None

    def is_job_done(self, response):
        # check jobId is present and equal to 'done'
        # return True on success, False otherwise
        try:
            job_id = response['jobs'][0]['jobId']
        except TypeError:
            job_id = None

        if job_id is not None and self.restApi.get_state(job_id) == 'done':
            return True
        return False

    def create_fileSystem(self):
        # Create fileSystem
        api = 'FileSystems'
        response, error = self.restApi.post(api, self.data)
        if not error:
            if self.is_job_done(response):
                return
            error = "Error: unexpected response on FileSystems create: %s" % str(
                response)
        self.module.fail_json(msg=error)

    def delete_fileSystem(self, fileSystemId):
        # Delete FileSystem
        api = 'FileSystems/' + fileSystemId
        self.data = None
        response, error = self.restApi.delete(api, self.data)
        if not error:
            if self.is_job_done(response):
                return
            error = "Error: unexpected response on FileSystems delete: %s" % str(
                response)
        self.module.fail_json(msg=error)

    def update_fileSystem(self, fileSystemId):
        # Update FileSystem
        api = 'FileSystems/' + fileSystemId
        response, error = self.restApi.put(api, self.data)
        if not error:
            if self.is_job_done(response):
                return
            error = "Error: unexpected response on FileSystems update: %s" % str(
                response)
        self.module.fail_json(msg=error)

    def apply(self):
        """
        Perform pre-checks, call functions and exit
        """

        fileSystem = None
        fileSystemId = self.get_filesystemId()

        if fileSystemId:
            # Getting the FileSystem details
            fileSystem = self.get_filesystem(fileSystemId)

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

        if cd_action is None and self.parameters['state'] == 'present':
            # Check if we need to update the fileSystem
            update_fileSystem = False
            if fileSystem['quotaInBytes'] is not None and 'quotaInBytes' in self.parameters \
                    and fileSystem['quotaInBytes'] != self.parameters['quotaInBytes']:
                update_fileSystem = True
            elif fileSystem['creationToken'] is not None and 'creationToken' in self.parameters \
                    and fileSystem['creationToken'] != self.parameters['creationToken']:
                update_fileSystem = True
            elif fileSystem['serviceLevel'] is not None and 'serviceLevel' in self.parameters \
                    and fileSystem['serviceLevel'] != self.parameters['serviceLevel']:
                update_fileSystem = True
            elif fileSystem['exportPolicy'][
                    'rules'] is not None and 'exportPolicy' in self.parameters:
                for rule_org in fileSystem['exportPolicy']['rules']:
                    for rule in self.parameters['exportPolicy']['rules']:
                        if rule_org['allowedClients'] != rule['allowedClients']:
                            update_fileSystem = True
                        elif rule_org['unixReadOnly'] != rule['unixReadOnly']:
                            update_fileSystem = True
                        elif rule_org['unixReadWrite'] != rule['unixReadWrite']:
                            update_fileSystem = True

            if update_fileSystem:
                self.na_helper.changed = True

        result_message = ""

        if self.na_helper.changed:
            if self.module.check_mode:
                # Skip changes
                result_message = "Check mode, skipping changes"
            else:
                if cd_action == "create":
                    self.create_fileSystem()
                    result_message = "FileSystem Created"
                elif cd_action == "delete":
                    self.delete_fileSystem(fileSystemId)
                    result_message = "FileSystem Deleted"
                else:  # modify
                    self.update_fileSystem(fileSystemId)
                    result_message = "FileSystem Updated"
        self.module.exit_json(changed=self.na_helper.changed,
                              msg=result_message)
class NetAppONTAPNVMENamespace(object):
    """
    Class with NVME namespace 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'),
                 ostype=dict(
                     required=False,
                     type='str',
                     choices=['windows', 'linux', 'vmware', 'xen', 'hyper_v']),
                 path=dict(required=True, type='str'),
                 size=dict(required=False, type='int')))

        self.module = AnsibleModule(argument_spec=self.argument_spec,
                                    required_if=[('state', 'present',
                                                  ['ostype', 'size'])],
                                    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_namespace(self):
        """
        Get current namespace details
        :return: dict if namespace exists, None otherwise
        """
        namespace_get = netapp_utils.zapi.NaElement('nvme-namespace-get-iter')
        query = {
            'query': {
                'nvme-namespace-info': {
                    'path': self.parameters['path'],
                    'vserver': self.parameters['vserver']
                }
            }
        }
        namespace_get.translate_struct(query)
        try:
            result = self.server.invoke_successfully(namespace_get,
                                                     enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error fetching namespace 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 result
        return None

    def create_namespace(self):
        """
        Create a NVME Namespace
        """
        options = {
            'path': self.parameters['path'],
            'ostype': self.parameters['ostype'],
            'size': self.parameters['size']
        }
        namespace_create = netapp_utils.zapi.NaElement('nvme-namespace-create')
        namespace_create.translate_struct(options)
        try:
            self.server.invoke_successfully(namespace_create,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error creating namespace for path %s: %s' %
                (self.parameters.get('path'), to_native(error)),
                exception=traceback.format_exc())

    def delete_namespace(self):
        """
        Delete a NVME Namespace
        """
        options = {'path': self.parameters['path']}
        namespace_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'nvme-namespace-delete', **options)
        try:
            self.server.invoke_successfully(namespace_delete,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error deleting namespace for path %s: %s' %
                (self.parameters.get('path'), to_native(error)),
                exception=traceback.format_exc())

    def apply(self):
        """
        Apply action to NVME Namespace
        """
        netapp_utils.ems_log_event("na_ontap_nvme_namespace", self.server)
        current = self.get_namespace()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_namespace()
                elif cd_action == 'delete':
                    self.delete_namespace()

        self.module.exit_json(changed=self.na_helper.changed)
class NetAppONTAPFirmwareUpgrade(object):
    """
    Class with ONTAP firmware upgrade 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', default='present'),
                 node=dict(required=False, type='str'),
                 firmware_type=dict(
                     required=True,
                     type='str',
                     choices=['service-processor', 'shelf', 'acp', 'disk']),
                 clear_logs=dict(required=False, type='bool', default=True),
                 package=dict(required=False, type='str'),
                 install_baseline_image=dict(required=False,
                                             type='bool',
                                             default=False),
                 update_type=dict(required=False, type='str'),
                 shelf_module_fw=dict(required=False, type='str'),
                 disk_fw=dict(required=False, type='str')))

        self.module = AnsibleModule(argument_spec=self.argument_spec,
                                    required_if=[
                                        ('firmware_type', 'acp', ['node']),
                                        ('firmware_type', 'disk', ['node']),
                                        ('firmware_type', 'service-processor',
                                         ['node', 'update_type']),
                                    ],
                                    supports_check_mode=True)

        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)
        if self.parameters.get('firmware_type') == 'service-processor':
            if self.parameters.get(
                    'install_baseline_image') and self.parameters.get(
                        'package') is not None:
                self.module.fail_json(
                    msg=
                    'Do not specify both package and install_baseline_image: true'
                )
            if not self.parameters.get('package') and self.parameters.get(
                    'install_baseline_image') == 'False':
                self.module.fail_json(
                    msg=
                    'Specify at least one of package or install_baseline_image'
                )
        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 firmware_image_get_iter(self):
        """
        Compose NaElement object to query current firmware version
        :return: NaElement object for firmware_image_get_iter with query
        """
        firmware_image_get = netapp_utils.zapi.NaElement(
            'service-processor-get-iter')
        query = netapp_utils.zapi.NaElement('query')
        firmware_image_info = netapp_utils.zapi.NaElement(
            'service-processor-info')
        firmware_image_info.add_new_child('node', self.parameters['node'])
        query.add_child_elem(firmware_image_info)
        firmware_image_get.add_child_elem(query)
        return firmware_image_get

    def firmware_image_get(self, node_name):
        """
        Get current firmware image info
        :return: True if query successful, else return None
        """
        firmware_image_get_iter = self.firmware_image_get_iter()
        try:
            result = self.server.invoke_successfully(firmware_image_get_iter,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error fetching firmware image details: %s: %s' %
                (self.parameters['node'], to_native(error)),
                exception=traceback.format_exc())
        # return firmware image details
        if result.get_child_by_name('num-records') and int(
                result.get_child_content('num-records')) > 0:
            sp_info = result.get_child_by_name(
                'attributes-list').get_child_by_name('service-processor-info')
            firmware_version = sp_info.get_child_content('firmware-version')
            return firmware_version
        return None

    def acp_firmware_required_get(self):
        """
        where acp firmware upgrade is required
        :return:  True is firmware upgrade is required else return None
        """
        acp_firmware_get_iter = netapp_utils.zapi.NaElement(
            'storage-shelf-acp-module-get-iter')
        query = netapp_utils.zapi.NaElement('query')
        acp_info = netapp_utils.zapi.NaElement('storage-shelf-acp-module')
        query.add_child_elem(acp_info)
        acp_firmware_get_iter.add_child_elem(query)
        try:
            result = self.server.invoke_successfully(acp_firmware_get_iter,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error fetching acp firmware details details: %s' %
                (to_native(error)),
                exception=traceback.format_exc())
        if result.get_child_by_name('attributes-list').get_child_by_name(
                'storage-shelf-acp-module'):
            acp_module_info = result.get_child_by_name(
                'attributes-list').get_child_by_name(
                    'storage-shelf-acp-module')
            state = acp_module_info.get_child_content('state')
            if state == 'firmware_update_required':
                # acp firmware version upgrade required
                return True
        return False

    def sp_firmware_image_update_progress_get(self, node_name):
        """
        Get current firmware image update progress info
        :return: Dictionary of firmware image update progress if query successful, else return None
        """
        firmware_update_progress_get = netapp_utils.zapi.NaElement(
            'service-processor-image-update-progress-get')
        firmware_update_progress_get.add_new_child('node',
                                                   self.parameters['node'])

        firmware_update_progress_info = dict()
        try:
            result = self.server.invoke_successfully(
                firmware_update_progress_get, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error fetching firmware image upgrade progress details: %s'
                % (to_native(error)),
                exception=traceback.format_exc())
        # return firmware image update progress details
        if result.get_child_by_name('attributes').get_child_by_name(
                'service-processor-image-update-progress-info'):
            update_progress_info = result.get_child_by_name(
                'attributes').get_child_by_name(
                    'service-processor-image-update-progress-info')
            firmware_update_progress_info[
                'is-in-progress'] = update_progress_info.get_child_content(
                    'is-in-progress')
            firmware_update_progress_info[
                'node'] = update_progress_info.get_child_content('node')
        return firmware_update_progress_info

    def shelf_firmware_info_get(self):
        """
        Get the current firmware of shelf module
        :return:dict with module id and firmware info
        """
        shelf_id_fw_info = dict()
        shelf_firmware_info_get = netapp_utils.zapi.NaElement(
            'storage-shelf-info-get-iter')
        desired_attributes = netapp_utils.zapi.NaElement('desired-attributes')
        storage_shelf_info = netapp_utils.zapi.NaElement('storage-shelf-info')
        shelf_module = netapp_utils.zapi.NaElement('shelf-modules')
        shelf_module_info = netapp_utils.zapi.NaElement(
            'storage-shelf-module-info')
        shelf_module.add_child_elem(shelf_module_info)
        storage_shelf_info.add_child_elem(shelf_module)
        desired_attributes.add_child_elem(storage_shelf_info)
        shelf_firmware_info_get.add_child_elem(desired_attributes)

        try:
            result = self.server.invoke_successfully(shelf_firmware_info_get,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error fetching shelf module firmware  details: %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:
            shelf_info = result.get_child_by_name(
                'attributes-list').get_child_by_name('storage-shelf-info')
            if (shelf_info.get_child_by_name('shelf-modules')
                    and shelf_info.get_child_by_name('shelf-modules').
                    get_child_by_name('storage-shelf-module-info')):
                shelves = shelf_info['shelf-modules'].get_children()
                for shelf in shelves:
                    shelf_id_fw_info[shelf.get_child_content(
                        'module-id')] = shelf.get_child_content(
                            'module-fw-revision')
        return shelf_id_fw_info

    def disk_firmware_info_get(self):
        """
        Get the current firmware of disks module
        :return:
        """
        disk_id_fw_info = dict()
        disk_firmware_info_get = netapp_utils.zapi.NaElement(
            'storage-disk-get-iter')
        desired_attributes = netapp_utils.zapi.NaElement('desired-attributes')
        storage_disk_info = netapp_utils.zapi.NaElement('storage-disk-info')
        disk_inv = netapp_utils.zapi.NaElement('disk-inventory-info')
        storage_disk_info.add_child_elem(disk_inv)
        desired_attributes.add_child_elem(storage_disk_info)
        disk_firmware_info_get.add_child_elem(desired_attributes)
        try:
            result = self.server.invoke_successfully(disk_firmware_info_get,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error fetching disk module firmware  details: %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:
            disk_info = result.get_child_by_name('attributes-list')
            disks = disk_info.get_children()
            for disk in disks:
                disk_id_fw_info[disk.get_child_content(
                    'disk-uid')] = disk.get_child_by_name(
                        'disk-inventory-info').get_child_content(
                            'firmware-revision')
        return disk_id_fw_info

    def disk_firmware_required_get(self):
        """
        Check weather disk firmware upgrade is required or not
        :return: True if the firmware upgrade is required
        """
        disk_firmware_info = self.disk_firmware_info_get()
        for disk in disk_firmware_info:
            if (disk_firmware_info[disk]) != self.parameters['disk_fw']:
                return True
        return False

    def shelf_firmware_required_get(self):
        """
        Check weather shelf firmware upgrade is required or not
        :return: True if the firmware upgrade is required
        """
        shelf_firmware_info = self.shelf_firmware_info_get()
        for module in shelf_firmware_info:
            if (shelf_firmware_info[module]
                ) != self.parameters['shelf_module_fw']:
                return True
        return False

    def sp_firmware_image_update(self):
        """
        Update current firmware image
        """
        firmware_update_info = netapp_utils.zapi.NaElement(
            'service-processor-image-update')
        if self.parameters.get('package') is not None:
            firmware_update_info.add_new_child('package',
                                               self.parameters['package'])
        if self.parameters.get('clear_logs') is not None:
            firmware_update_info.add_new_child(
                'clear-logs', str(self.parameters['clear_logs']))
        if self.parameters.get('install_baseline_image') is not None:
            firmware_update_info.add_new_child(
                'install-baseline-image',
                str(self.parameters['install_baseline_image']))
        firmware_update_info.add_new_child('node', self.parameters['node'])
        firmware_update_info.add_new_child('update-type',
                                           self.parameters['update_type'])

        try:
            self.server.invoke_successfully(firmware_update_info,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            # Current firmware version matches the version to be installed
            if to_native(error.code) == '13001' and (error.message.startswith(
                    'Service Processor update skipped')):
                return False
            self.module.fail_json(
                msg='Error updating firmware image for %s: %s' %
                (self.parameters['node'], to_native(error)),
                exception=traceback.format_exc())
        return True

    def shelf_firmware_upgrade(self):
        """
        Upgrade shelf firmware image
        """
        shelf_firmware_update_info = netapp_utils.zapi.NaElement(
            'storage-shelf-firmware-update')
        try:
            self.server.invoke_successfully(shelf_firmware_update_info,
                                            enable_tunneling=True)
            return True
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error updating shelf firmware image : %s' %
                (to_native(error)),
                exception=traceback.format_exc())

    def acp_firmware_upgrade(self):
        """
        Upgrade shelf firmware image
        """
        acp_firmware_update_info = netapp_utils.zapi.NaElement(
            'storage-shelf-acp-firmware-update')
        acp_firmware_update_info.add_new_child('node-name',
                                               self.parameters['node'])
        try:
            self.server.invoke_successfully(acp_firmware_update_info,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error updating acp firmware image : %s' %
                (to_native(error)),
                exception=traceback.format_exc())

    def disk_firmware_upgrade(self):
        """
        Upgrade disk firmware
        """
        disk_firmware_update_info = netapp_utils.zapi.NaElement(
            'disk-update-disk-fw')
        disk_firmware_update_info.add_new_child('node-name',
                                                self.parameters['node'])
        try:
            self.server.invoke_successfully(disk_firmware_update_info,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error updating disk firmware image : %s' %
                (to_native(error)),
                exception=traceback.format_exc())
        return True

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

    def apply(self):
        """
        Apply action to upgrade firmware
        """
        changed = False
        self.autosupport_log()
        firmware_update_progress = dict()
        if self.parameters.get('firmware_type') == 'service-processor':
            # service-processor firmware upgrade
            current = self.firmware_image_get(self.parameters['node'])

            if self.parameters.get('state') == 'present' and current:
                if not self.module.check_mode:
                    if self.sp_firmware_image_update():
                        changed = True
                    firmware_update_progress = self.sp_firmware_image_update_progress_get(
                        self.parameters['node'])
                    while firmware_update_progress.get(
                            'is-in-progress') == 'true':
                        time.sleep(25)
                        firmware_update_progress = self.sp_firmware_image_update_progress_get(
                            self.parameters['node'])
                else:
                    # we don't know until we try the upgrade
                    changed = True

        elif self.parameters.get('firmware_type') == 'shelf':
            # shelf firmware upgrade
            if self.parameters.get('shelf_module_fw'):
                if self.shelf_firmware_required_get():
                    if not self.module.check_mode:
                        changed = self.shelf_firmware_upgrade()
                    else:
                        changed = True
            else:
                if not self.module.check_mode:
                    changed = self.shelf_firmware_upgrade()
                else:
                    # we don't know until we try the upgrade -- assuming the worst
                    changed = True
        elif self.parameters.get('firmware_type') == 'acp':
            # acp firmware upgrade
            if self.acp_firmware_required_get():
                if not self.module.check_mode:
                    self.acp_firmware_upgrade()
                changed = True
        elif self.parameters.get('firmware_type') == 'disk':
            # Disk firmware upgrade
            if self.parameters.get('disk_fw'):
                if self.disk_firmware_required_get():
                    if not self.module.check_mode:
                        changed = self.disk_firmware_upgrade()
                    else:
                        changed = True
            else:
                if not self.module.check_mode:
                    changed = self.disk_firmware_upgrade()
                else:
                    # we don't know until we try the upgrade -- assuming the worst
                    changed = True

        self.module.exit_json(changed=changed)
class NetAppONTAPVserverPeer(object):
    """
    Class with vserver peer 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'),
                 peer_vserver=dict(required=True, type='str'),
                 peer_cluster=dict(required=False, type='str'),
                 applications=dict(required=False,
                                   type='list',
                                   choices=[
                                       'snapmirror', 'file_copy', 'lun_copy',
                                       'flexcache'
                                   ]),
                 dest_hostname=dict(required=False, type='str'),
                 dest_username=dict(required=False, type='str'),
                 dest_password=dict(required=False, type='str', no_log=True)))

        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)
            if self.parameters.get('dest_hostname'):
                self.module.params['hostname'] = self.parameters[
                    'dest_hostname']
                if self.parameters.get('dest_username'):
                    self.module.params['username'] = self.parameters[
                        'dest_username']
                if self.parameters.get('dest_password'):
                    self.module.params['password'] = self.parameters[
                        'dest_password']
                self.dest_server = netapp_utils.setup_na_ontap_zapi(
                    module=self.module)
                # reset to source host connection for asup logs
                self.module.params['hostname'] = self.parameters['hostname']

    def vserver_peer_get_iter(self):
        """
        Compose NaElement object to query current vserver using peer-vserver and vserver parameters
        :return: NaElement object for vserver-get-iter with query
        """
        vserver_peer_get = netapp_utils.zapi.NaElement('vserver-peer-get-iter')
        query = netapp_utils.zapi.NaElement('query')
        vserver_peer_info = netapp_utils.zapi.NaElement('vserver-peer-info')
        vserver_peer_info.add_new_child('peer-vserver',
                                        self.parameters['peer_vserver'])
        vserver_peer_info.add_new_child('vserver', self.parameters['vserver'])
        query.add_child_elem(vserver_peer_info)
        vserver_peer_get.add_child_elem(query)
        return vserver_peer_get

    def vserver_peer_get(self):
        """
        Get current vserver peer info
        :return: Dictionary of current vserver peer details if query successful, else return None
        """
        vserver_peer_get_iter = self.vserver_peer_get_iter()
        vserver_info = dict()
        try:
            result = self.server.invoke_successfully(vserver_peer_get_iter,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error fetching vserver peer %s: %s' %
                (self.parameters['vserver'], to_native(error)),
                exception=traceback.format_exc())
        # return vserver peer details
        if result.get_child_by_name('num-records') and \
                int(result.get_child_content('num-records')) > 0:
            vserver_peer_info = result.get_child_by_name(
                'attributes-list').get_child_by_name('vserver-peer-info')
            vserver_info['peer_vserver'] = vserver_peer_info.get_child_content(
                'peer-vserver')
            vserver_info['vserver'] = vserver_peer_info.get_child_content(
                'vserver')
            vserver_info['peer_state'] = vserver_peer_info.get_child_content(
                'peer-state')
            return vserver_info
        return None

    def vserver_peer_delete(self):
        """
        Delete a vserver peer
        """
        vserver_peer_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'vserver-peer-delete', **{
                'peer-vserver': self.parameters['peer_vserver'],
                'vserver': self.parameters['vserver']
            })
        try:
            self.server.invoke_successfully(vserver_peer_delete,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error deleting vserver peer %s: %s' %
                (self.parameters['vserver'], to_native(error)),
                exception=traceback.format_exc())

    def get_peer_cluster_name(self):
        """
        Get local cluster name
        :return: cluster name
        """
        cluster_info = netapp_utils.zapi.NaElement('cluster-identity-get')
        try:
            result = self.server.invoke_successfully(cluster_info,
                                                     enable_tunneling=True)
            return result.get_child_by_name('attributes').get_child_by_name(
                'cluster-identity-info').get_child_content('cluster-name')
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error fetching peer cluster name for peer vserver %s: %s'
                % (self.parameters['peer_vserver'], to_native(error)),
                exception=traceback.format_exc())

    def vserver_peer_create(self):
        """
        Create a vserver peer
        """
        if self.parameters.get('applications') is None:
            self.module.fail_json(msg='applications parameter is missing')
        if self.parameters.get(
                'peer_cluster'
        ) is not None and self.parameters.get('dest_hostname') is None:
            self.module.fail_json(
                msg=
                'dest_hostname is required for peering a vserver in remote cluster'
            )
        if self.parameters.get('peer_cluster') is None:
            self.parameters['peer_cluster'] = self.get_peer_cluster_name()
        vserver_peer_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'vserver-peer-create', **{
                'peer-vserver': self.parameters['peer_vserver'],
                'vserver': self.parameters['vserver'],
                'peer-cluster': self.parameters['peer_cluster']
            })
        applications = netapp_utils.zapi.NaElement('applications')
        for application in self.parameters['applications']:
            applications.add_new_child('vserver-peer-application', application)
        vserver_peer_create.add_child_elem(applications)
        try:
            self.server.invoke_successfully(vserver_peer_create,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error creating vserver peer %s: %s' %
                (self.parameters['vserver'], to_native(error)),
                exception=traceback.format_exc())

    def is_remote_peer(self):
        if self.parameters.get('dest_hostname') is None or \
                (self.parameters['dest_hostname'] == self.parameters['hostname']):
            return False
        return True

    def vserver_peer_accept(self):
        """
        Accept a vserver peer at destination
        """
        # peer-vserver -> remote (source vserver is provided)
        # vserver -> local (destination vserver is provided)
        vserver_peer_accept = netapp_utils.zapi.NaElement.create_node_with_children(
            'vserver-peer-accept', **{
                'peer-vserver': self.parameters['vserver'],
                'vserver': self.parameters['peer_vserver']
            })
        try:
            self.dest_server.invoke_successfully(vserver_peer_accept,
                                                 enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error accepting vserver peer %s: %s' %
                (self.parameters['peer_vserver'], to_native(error)),
                exception=traceback.format_exc())

    def apply(self):
        """
        Apply action to create/delete or accept vserver peer
        """
        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_vserver_peer", cserver)
        current = self.vserver_peer_get()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if cd_action == 'create':
            self.vserver_peer_create()
            # accept only if the peer relationship is on a remote cluster
            if self.is_remote_peer():
                self.vserver_peer_accept()
        elif cd_action == 'delete':
            self.vserver_peer_delete()

        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)
예제 #29
0
class ElementSWVlan(object):
    """ class to handle VLAN operations """

    def __init__(self):
        """
            Setup Ansible parameters and ElementSW connection
        """
        self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
        self.argument_spec.update(dict(
            state=dict(required=False, choices=['present', 'absent'],
                       default='present'),
            name=dict(required=False, type='str'),
            vlan_tag=dict(required=True, type='str'),
            svip=dict(required=False, type='str'),
            netmask=dict(required=False, type='str'),
            gateway=dict(required=False, type='str'),
            namespace=dict(required=False, type='bool'),
            attributes=dict(required=False, type='dict'),
            address_blocks=dict(required=False, type='list')
        ))

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

        if HAS_SF_SDK is False:
            self.module.fail_json(msg="Unable to import the SolidFire Python SDK")
        else:
            self.elem = netapp_utils.create_sf_connection(module=self.module)

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

        self.elementsw_helper = NaElementSWModule(self.elem)

        # add telemetry attributes
        if self.parameters.get('attributes') is not None:
            self.parameters['attributes'].update(self.elementsw_helper.set_element_attributes(source='na_elementsw_vlan'))
        else:
            self.parameters['attributes'] = self.elementsw_helper.set_element_attributes(source='na_elementsw_vlan')

    def validate_keys(self):
        """
            Validate if all required keys are present before creating
        """
        required_keys = ['address_blocks', 'svip', 'netmask', 'name']
        if all(item in self.parameters.keys() for item in required_keys) is False:
            self.module.fail_json(msg="One or more required fields %s for creating VLAN is missing"
                                      % required_keys)
        addr_blk_fields = ['start', 'size']
        for address in self.parameters['address_blocks']:
            if 'start' not in address or 'size' not in address:
                self.module.fail_json(msg="One or more required fields %s for address blocks is missing"
                                          % addr_blk_fields)

    def create_network(self):
        """
            Add VLAN
        """
        try:
            self.validate_keys()
            create_params = self.parameters.copy()
            for key in ['username', 'hostname', 'password', 'state', 'vlan_tag']:
                del create_params[key]
            self.elem.add_virtual_network(virtual_network_tag=self.parameters['vlan_tag'], **create_params)
        except solidfire.common.ApiServerError as err:
            self.module.fail_json(msg="Error creating VLAN %s"
                                  % self.parameters['vlan_tag'],
                                  exception=to_native(err))

    def delete_network(self):
        """
            Remove VLAN
        """
        try:
            self.elem.remove_virtual_network(virtual_network_tag=self.parameters['vlan_tag'])
        except solidfire.common.ApiServerError as err:
            self.module.fail_json(msg="Error deleting VLAN %s"
                                  % self.parameters['vlan_tag'],
                                  exception=to_native(err))

    def modify_network(self, modify):
        """
            Modify the VLAN
        """
        try:
            self.elem.modify_virtual_network(virtual_network_tag=self.parameters['vlan_tag'], **modify)
        except solidfire.common.ApiServerError as err:
            self.module.fail_json(msg="Error modifying VLAN %s"
                                  % self.parameters['vlan_tag'],
                                  exception=to_native(err))

    def get_network_details(self):
        """
            Check existing VLANs
            :return: vlan details if found, None otherwise
            :type: dict
        """
        vlans = self.elem.list_virtual_networks(virtual_network_tag=self.parameters['vlan_tag'])
        vlan_details = dict()
        for vlan in vlans.virtual_networks:
            if vlan is not None:
                vlan_details['name'] = vlan.name
                vlan_details['address_blocks'] = list()
                for address in vlan.address_blocks:
                    vlan_details['address_blocks'].append({
                        'start': address.start,
                        'size': address.size
                    })
                vlan_details['svip'] = vlan.svip
                vlan_details['gateway'] = vlan.gateway
                vlan_details['netmask'] = vlan.netmask
                vlan_details['namespace'] = vlan.namespace
                vlan_details['attributes'] = vlan.attributes
                return vlan_details
        return None

    def apply(self):
        """
            Call create / delete / modify vlan methods
        """
        network = self.get_network_details()
        # calling helper to determine action
        cd_action = self.na_helper.get_cd_action(network, self.parameters)
        modify = self.na_helper.get_modified_attributes(network, self.parameters)
        if cd_action == "create":
            self.create_network()
        elif cd_action == "delete":
            self.delete_network()
        elif modify:
            self.modify_network(modify)
        self.module.exit_json(changed=self.na_helper.changed)
예제 #30
0
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)