class NetAppOntapIgroup(object):
    def __init__(self):

        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 name=dict(required=True, type='str'),
                 from_name=dict(required=False, type='str', default=None),
                 ostype=dict(required=False, type='str'),
                 initiator_group_type=dict(required=False,
                                           type='str',
                                           choices=['fcp', 'iscsi', 'mixed']),
                 initiators=dict(required=False,
                                 type='list',
                                 aliases=['initiator']),
                 vserver=dict(required=True, type='str'),
                 force_remove_initiator=dict(required=False,
                                             type='bool',
                                             default=False),
                 bind_portset=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['vserver'])

    def get_igroup(self, name):
        """
        Return details about the igroup
        :param:
            name : Name of the igroup

        :return: Details about the igroup. None if not found.
        :rtype: dict
        """
        igroup_info = netapp_utils.zapi.NaElement('igroup-get-iter')
        attributes = dict(
            query={
                'initiator-group-info': {
                    'initiator-group-name': name,
                    'vserver': self.parameters['vserver']
                }
            })
        igroup_info.translate_struct(attributes)
        result, current = None, 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['name'], to_native(error)),
                                  exception=traceback.format_exc())
        if result.get_child_by_name('num-records') and int(
                result.get_child_content('num-records')) >= 1:
            igroup = result.get_child_by_name(
                'attributes-list').get_child_by_name('initiator-group-info')
            initiators = []
            if igroup.get_child_by_name('initiators'):
                current_initiators = igroup['initiators'].get_children()
                for initiator in current_initiators:
                    initiators.append(initiator['initiator-name'])
            current = {'initiators': initiators}

        return current

    def add_initiators(self):
        """
        Add the list of initiators to igroup
        :return: None
        """
        # don't add if initiators is empty string
        if self.parameters.get('initiators') == [
                ''
        ] or self.parameters.get('initiators') is None:
            return
        for initiator in self.parameters['initiators']:
            self.modify_initiator(initiator, 'igroup-add')

    def remove_initiators(self, initiators):
        """
        Removes all existing initiators from igroup
        :return: None
        """
        for initiator in initiators:
            self.modify_initiator(initiator, 'igroup-remove')

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

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

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

    def create_igroup(self):
        """
        Create the igroup.
        """
        options = {'initiator-group-name': self.parameters['name']}
        if self.parameters.get('ostype') is not None:
            options['os-type'] = self.parameters['ostype']
        if self.parameters.get('initiator_group_type') is not None:
            options['initiator-group-type'] = self.parameters[
                'initiator_group_type']
        if self.parameters.get('bind_portset') is not None:
            options['bind-portset'] = self.parameters['bind_portset']

        igroup_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'igroup-create', **options)

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

    def delete_igroup(self):
        """
        Delete the igroup.
        """
        igroup_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'igroup-destroy', **{
                'initiator-group-name':
                self.parameters['name'],
                'force':
                'true'
                if self.parameters['force_remove_initiator'] else 'false'
            })

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

    def rename_igroup(self):
        """
        Rename the igroup.
        """
        igroup_rename = netapp_utils.zapi.NaElement.create_node_with_children(
            'igroup-rename', **{
                'initiator-group-name': self.parameters['from_name'],
                'initiator-group-new-name': str(self.parameters['name'])
            })
        try:
            self.server.invoke_successfully(igroup_rename,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error renaming igroup %s: %s' %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

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

    def apply(self):
        self.autosupport_log()
        current = self.get_igroup(self.parameters['name'])
        # rename and create are mutually exclusive
        rename, cd_action, modify = None, None, None
        if self.parameters.get('from_name'):
            rename = self.na_helper.is_rename_action(
                self.get_igroup(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_igroup()
                elif cd_action == 'create':
                    self.create_igroup()
                elif cd_action == 'delete':
                    self.delete_igroup()
                if modify:
                    self.remove_initiators(current['initiators'])
                    self.add_initiators()
        self.module.exit_json(changed=self.na_helper.changed)
Exemple #2
0
class NetAppONTAPMotd(object):
    def __init__(self):
        argument_spec = netapp_utils.na_ontap_host_argument_spec()
        argument_spec.update(
            dict(state=dict(required=False,
                            default='present',
                            choices=['present', 'absent']),
                 vserver=dict(required=True, type='str'),
                 message=dict(default='', type='str'),
                 show_cluster_motd=dict(default=True, type='bool')))

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

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

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

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

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

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

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

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

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                self.modify_motd()
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapSnapshotPolicy(object):
    """
    Creates and deletes a Snapshot Policy
    """
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(
                state=dict(required=False,
                           choices=['present', 'absent'],
                           default='present'),
                name=dict(required=True, type="str"),
                enabled=dict(required=False, type="bool"),
                # count is a list of integers
                count=dict(required=False, type="list", elements="int"),
                comment=dict(required=False, type="str"),
                schedule=dict(required=False, type="list", elements="str"),
                snapmirror_label=dict(required=False,
                                      type="list",
                                      elements="str"),
                vserver=dict(required=False, type="str")))
        self.module = AnsibleModule(argument_spec=self.argument_spec,
                                    required_if=[
                                        ('state', 'present',
                                         ['enabled', 'count', 'schedule']),
                                    ],
                                    supports_check_mode=True)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def asup_log_for_cserver(self, event_name):
        """
        Fetch admin vserver for the given cluster
        Create and Autosupport log event with the given module name
        :param event_name: Name of the event log
        :return: None
        """
        results = netapp_utils.get_cserver(self.server)
        cserver = netapp_utils.setup_na_ontap_zapi(module=self.module,
                                                   vserver=results)
        netapp_utils.ems_log_event(event_name, cserver)

    def apply(self):
        """
        Check to see which play we should run
        """
        self.asup_log_for_cserver("na_ontap_snapshot_policy")
        current = self.get_snapshot_policy()
        modify = None
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if cd_action is None and self.parameters['state'] == 'present':
            # Don't sort schedule/count/snapmirror_label lists as it can
            # mess up the intended parameter order.
            modify = self.na_helper.get_modified_attributes(
                current, self.parameters)

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_snapshot_policy()
                elif cd_action == 'delete':
                    self.delete_snapshot_policy()
                if modify:
                    self.modify_snapshot_policy(current)
                    self.modify_snapshot_policy_schedules(current)
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppONTAPNVMe(object):
    """
    Class with NVMe service methods
    """
    def __init__(self):

        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            type='str',
                            choices=['present', 'absent'],
                            default='present'),
                 vserver=dict(required=True, type='str'),
                 status_admin=dict(required=False, type='bool')))

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

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

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

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

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

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

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

    def apply(self):
        """
        Apply action to NVMe service
        """
        netapp_utils.ems_log_event("na_ontap_nvme", self.server)
        current = self.get_nvme()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if self.parameters.get('status_admin') is not None:
            self.parameters[
                'status_admin'] = self.na_helper.get_value_for_bool(
                    False, self.parameters['status_admin'])
            if cd_action is None and self.parameters['state'] == 'present':
                modify = self.na_helper.get_modified_attributes(
                    current, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_nvme()
                elif cd_action == 'delete':
                    # NVMe status_admin needs to be down before deleting it
                    self.modify_nvme('false')
                    self.delete_nvme()
                elif modify:
                    self.modify_nvme()

        self.module.exit_json(changed=self.na_helper.changed)
Exemple #5
0
class NetAppontapExportRule(object):
    ''' object initialize and class methods '''

    def __init__(self):

        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(dict(
            state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
            name=dict(required=True, type='str', aliases=['policy_name']),
            protocol=dict(required=False,
                          type='list', default=None,
                          choices=['any', 'nfs', 'nfs3', 'nfs4', 'cifs', 'flexcache']),
            client_match=dict(required=False, type='list'),
            ro_rule=dict(required=False,
                         type='list', default=None,
                         choices=['any', 'none', 'never', 'krb5', 'krb5i', 'krb5p', 'ntlm', 'sys']),
            rw_rule=dict(required=False,
                         type='list', default=None,
                         choices=['any', 'none', 'never', 'krb5', 'krb5i', 'krb5p', 'ntlm', 'sys']),
            super_user_security=dict(required=False,
                                     type='list', default=None,
                                     choices=['any', 'none', 'never', 'krb5', 'krb5i', 'krb5p', 'ntlm', 'sys']),
            allow_suid=dict(required=False, type='bool'),
            rule_index=dict(required=False, type='int'),
            vserver=dict(required=True, type='str'),
        ))

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

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

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

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

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

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

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

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

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

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

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

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

        return None

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

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

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

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

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

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

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

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

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

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                # create export policy (if policy doesn't exist) only when changed=True
                if not self.get_export_policy():
                    self.create_export_policy()
                if action == 'create':
                    self.create_export_policy_rule()
                elif action == 'delete':
                    if current['num_records'] > 1:
                        self.module.fail_json(msg='Multiple export policy rules exist.'
                                                  'Please specify a rule_index to delete')
                    self.delete_export_policy_rule(current['rule_index'])
                elif modify:
                    self.modify_export_policy_rule(modify)
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapSecurityKeyManager(object):
    '''class with key manager operations'''
    def __init__(self):
        '''Initialize module parameters'''
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(state=dict(required=False,
                                             choices=['present', 'absent'],
                                             default='present'),
                                  ip_address=dict(required=True, type='str'),
                                  node=dict(required=False, type='str'),
                                  tcp_port=dict(required=False,
                                                type='int',
                                                default=5696))
        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.cluster = netapp_utils.setup_na_ontap_zapi(module=self.module)

    def get_key_manager(self):
        """
        get key manager by ip address.
        :return: a dict of key manager
        """
        key_manager_info = netapp_utils.zapi.NaElement(
            'security-key-manager-get-iter')
        query_details = netapp_utils.zapi.NaElement.create_node_with_children(
            'key-manager-info',
            **{'key-manager-ip-address': self.parameters['ip_address']})
        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(query_details)
        key_manager_info.add_child_elem(query)

        try:
            result = self.cluster.invoke_successfully(key_manager_info,
                                                      enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error fetching key manager %s : %s' %
                                  (self.parameters['node'], to_native(error)),
                                  exception=traceback.format_exc())

        return_value = None
        if result.get_child_by_name('num-records') and int(
                result.get_child_content('num-records')) > 0:
            key_manager = result.get_child_by_name(
                'attributes-list').get_child_by_name('key-manager-info')
            return_value = {}
            if key_manager.get_child_by_name('key-manager-ip-address'):
                return_value['ip_address'] = key_manager.get_child_content(
                    'key-manager-ip-address')
            if key_manager.get_child_by_name('key-manager-server-status'):
                return_value['server_status'] = key_manager.get_child_content(
                    'key-manager-server-status')
            if key_manager.get_child_by_name('key-manager-tcp-port'):
                return_value['tcp_port'] = key_manager.get_child_content(
                    'key-manager-tcp-port')
            if key_manager.get_child_by_name('node-name'):
                return_value['node'] = key_manager.get_child_content(
                    'node-name')

        return return_value

    def key_manager_setup(self):
        """
        set up external key manager.
        """
        key_manager_setup = netapp_utils.zapi.NaElement(
            'security-key-manager-setup')
        # if specify on-boarding passphrase, it is on-boarding key management.
        # it not, then it's external key management.
        try:
            self.cluster.invoke_successfully(key_manager_setup, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error setting up key manager %s : %s' %
                                  (self.parameters['node'], to_native(error)),
                                  exception=traceback.format_exc())

    def create_key_manager(self):
        """
        add key manager.
        """
        key_manager_create = netapp_utils.zapi.NaElement(
            'security-key-manager-add')
        key_manager_create.add_new_child('key-manager-ip-address',
                                         self.parameters['ip_address'])
        if self.parameters.get('tcp_port'):
            key_manager_create.add_new_child('key-manager-tcp-port',
                                             str(self.parameters['tcp_port']))
        try:
            self.cluster.invoke_successfully(key_manager_create, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error creating key manager %s : %s' %
                                  (self.parameters['node'], to_native(error)),
                                  exception=traceback.format_exc())

    def delete_key_manager(self):
        """
        delete key manager.
        """
        key_manager_delete = netapp_utils.zapi.NaElement(
            'security-key-manager-delete')
        key_manager_delete.add_new_child('key-manager-ip-address',
                                         self.parameters['ip_address'])
        try:
            self.cluster.invoke_successfully(key_manager_delete, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error deleting key manager %s : %s' %
                                  (self.parameters['node'], to_native(error)),
                                  exception=traceback.format_exc())

    def apply(self):
        self.asup_log_for_cserver("na_ontap_security_key_manager")
        self.key_manager_setup()
        current = self.get_key_manager()
        cd_action = None
        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_key_manager()
                elif cd_action == 'delete':
                    self.delete_key_manager()
        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.cluster)
        cserver = netapp_utils.setup_na_ontap_zapi(module=self.module,
                                                   vserver=results)
        netapp_utils.ems_log_event(event_name, cserver)
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)
Exemple #8
0
class NetAppOntapNetRoutes(object):
    """
    Create, Modifies and Destroys a Net Route
    """
    def __init__(self):
        """
        Initialize the Ontap Net Route 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'),
                vserver=dict(required=True, type='str'),
                destination=dict(required=True, type='str'),
                gateway=dict(required=True, type='str'),
                metric=dict(required=False, type='str'),
                from_destination=dict(required=False, type='str',
                                      default=None),
                from_gateway=dict(required=False, type='str', default=None),
                from_metric=dict(required=False, type='str', default=None),
            ))

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

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

        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_net_route(self, current_metric=None):
        """
        Creates a new Route
        """
        route_obj = netapp_utils.zapi.NaElement('net-routes-create')
        route_obj.add_new_child("destination", self.parameters['destination'])
        route_obj.add_new_child("gateway", self.parameters['gateway'])
        if current_metric is None and self.parameters.get(
                'metric') is not None:
            metric = self.parameters['metric']
        else:
            metric = current_metric
        # Metric can be None, Can't set metric to none
        if metric is not None:
            route_obj.add_new_child("metric", metric)
        try:
            self.server.invoke_successfully(route_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error creating net route: %s' %
                                  (to_native(error)),
                                  exception=traceback.format_exc())

    def delete_net_route(self, params=None):
        """
        Deletes a given Route
        """
        route_obj = netapp_utils.zapi.NaElement('net-routes-destroy')
        if params is None:
            params = self.parameters
        route_obj.add_new_child("destination", params['destination'])
        route_obj.add_new_child("gateway", params['gateway'])
        try:
            self.server.invoke_successfully(route_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error deleting net route: %s' %
                                  (to_native(error)),
                                  exception=traceback.format_exc())

    def modify_net_route(self, current, desired):
        """
        Modify a net route
        """
        # return if there is nothing to change
        for key, val in desired.items():
            if val != current[key]:
                self.na_helper.changed = True
                break
        if not self.na_helper.changed:
            return
        # delete and re-create with new params
        self.delete_net_route(current)
        route_obj = netapp_utils.zapi.NaElement('net-routes-create')
        for attribute in ['metric', 'destination', 'gateway']:
            if desired.get(attribute) is not None:
                value = desired[attribute]
            else:
                value = current[attribute]
            route_obj.add_new_child(attribute, value)
        try:
            result = self.server.invoke_successfully(route_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            # restore the old route, create the route with the existing metric
            self.create_net_route(current['metric'])
            # return if desired route already exists
            if to_native(error.code) == '13001':
                return
            # Invalid value specified for any of the attributes
            self.module.fail_json(msg='Error modifying net route: %s' %
                                  (to_native(error)),
                                  exception=traceback.format_exc())

    def get_net_route(self, params=None):
        """
        Checks to see if a route exist or not
        :return: NaElement object if a route exists, None otherwise
        """
        if params is not None:
            # we need at least on of the new_destination or new_gateway to fetch desired route
            if params.get('destination') is None and params.get(
                    'gateway') is None:
                return None
        current = None
        route_obj = netapp_utils.zapi.NaElement('net-routes-get')
        for attr in ['destination', 'gateway']:
            if params and params.get(attr) is not None:
                value = params[attr]
            else:
                value = self.parameters[attr]
            route_obj.add_new_child(attr, value)
        try:
            result = self.server.invoke_successfully(route_obj, True)
            if result.get_child_by_name('attributes') is not None:
                route_info = result.get_child_by_name(
                    'attributes').get_child_by_name('net-vs-routes-info')
                current = {
                    'destination': route_info.get_child_content('destination'),
                    'gateway': route_info.get_child_content('gateway'),
                    'metric': route_info.get_child_content('metric')
                }

        except netapp_utils.zapi.NaApiError as error:
            # Error 13040 denotes a route doesn't exist.
            if to_native(error.code) == "15661":
                return None
            self.module.fail_json(msg='Error fetching net route: %s' %
                                  (to_native(error)),
                                  exception=traceback.format_exc())
        return current

    def is_modify_action(self, current, desired):
        """
        Get desired action to be applied for net routes
        Destination and gateway are unique params for a route and cannot be duplicated
        So if a route with desired destination or gateway exists already, we don't try to modify
        :param current: current details
        :param desired: desired details
        :return: create / delete / modify / None
        """
        if current is None and desired is None:
            # this is invalid
            # cannot modify a non existent resource
            return None
        if current is None and desired is not None:
            # idempotency or duplication
            # we need not create
            return False
        if current is not None and desired is not None:
            # we can't modify an ambiguous route (idempotency/duplication)
            return False
        return True

    def get_params_to_be_modified(self, current):
        """
        Get parameters and values that need to be modified
        :param current: current details
        :return: dict(), None
        """
        if current is None:
            return None
        desired = dict()
        if self.parameters.get('new_destination') is not None and \
                self.parameters['new_destination'] != current['destination']:
            desired['destination'] = self.parameters['new_destination']
        if self.parameters.get('new_gateway') is not None and \
                self.parameters['new_gateway'] != current['gateway']:
            desired['gateway'] = self.parameters['new_gateway']
        if self.parameters.get('new_metric') is not None and \
                self.parameters['new_metric'] != current['metric']:
            desired['metric'] = self.parameters['new_metric']
        return desired

    def apply(self):
        """
        Run Module based on play book
        """
        netapp_utils.ems_log_event("na_ontap_net_routes", self.server)
        current = self.get_net_route()
        modify, cd_action = None, None
        modify_params = {
            'destination': self.parameters.get('from_destination'),
            'gateway': self.parameters.get('from_gateway'),
            'metric': self.parameters.get('from_metric')
        }
        # if any from_* param is present in playbook, check for modify action
        if any(modify_params.values()):
            # destination and gateway combination is unique, and is considered like a id. so modify destination
            # or gateway is considered a rename action. metric is considered an attribute of the route so it is
            # considered as modify.
            if modify_params.get('metric') is not None:
                modify = True
                old_params = current
            else:
                # get parameters that are eligible for modify
                old_params = self.get_net_route(modify_params)
                modify = self.na_helper.is_rename_action(old_params, current)
            if modify is None:
                self.module.fail_json(
                    msg="Error modifying: route %s does not exist" %
                    self.parameters['from_destination'])
        else:
            cd_action = self.na_helper.get_cd_action(current, self.parameters)

        if cd_action == 'create':
            self.create_net_route()
        elif cd_action == 'delete':
            self.delete_net_route()
        elif modify:
            desired = {}
            for key, value in old_params.items():
                desired[key] = value
            for key, value in modify_params.items():
                if value is not None:
                    desired[key] = self.parameters.get(key)
            self.modify_net_route(old_params, desired)
        self.module.exit_json(changed=self.na_helper.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)
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 NetAppONTAPFirewallPolicy(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'),
                 allow_list=dict(required=False, type="list"),
                 policy=dict(required=False, type='str'),
                 service=dict(required=False,
                              type='str',
                              choices=[
                                  'dns', 'http', 'https', 'ndmp', 'ndmps',
                                  'ntp', 'rsh', 'snmp', 'ssh', 'telnet'
                              ]),
                 vserver=dict(required=False, type="str"),
                 enable=dict(required=False,
                             type="str",
                             choices=['enable', 'disable']),
                 logging=dict(required=False,
                              type="str",
                              choices=['enable', 'disable']),
                 node=dict(required=False, type="str")))

        self.module = AnsibleModule(
            argument_spec=self.argument_spec,
            required_together=(['policy', 'service',
                                'vserver'], ['enable', 'node']),
            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 HAS_IPADDRESS_LIB is False:
            self.module.fail_json(
                msg="the python ipaddress lib is required for this module")
        return

    def validate_ip_addresses(self):
        '''
            Validate if the given IP address is a network address (i.e. it's host bits are set to 0)
            ONTAP doesn't validate if the host bits are set,
            and hence doesn't add a new address unless the IP is from a different network.
            So this validation allows the module to be idempotent.
            :return: None
        '''
        for ip in self.parameters['allow_list']:
            # create an IPv4 object for current IP address
            if sys.version_info[0] >= 3:
                ip_addr = str(ip)
            else:
                ip_addr = unicode(ip)  # pylint: disable=undefined-variable
            # get network address from netmask, throw exception if address is not a network address
            try:
                ipaddress.ip_network(ip_addr)
            except ValueError as exc:
                self.module.fail_json(
                    msg=
                    'Error: Invalid IP address value for allow_list parameter.'
                    'Please specify a network address without host bits set: %s'
                    % (to_native(exc)))

    def get_firewall_policy(self):
        """
        Get a firewall policy
        :return: returns a firewall policy object, or returns False if there are none
        """
        net_firewall_policy_obj = netapp_utils.zapi.NaElement(
            "net-firewall-policy-get-iter")
        attributes = {
            'query': {
                'net-firewall-policy-info': self.firewall_policy_attributes()
            }
        }
        net_firewall_policy_obj.translate_struct(attributes)

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

        if result.get_child_by_name('num-records') and int(
                result.get_child_content('num-records')) >= 1:
            attributes_list = result.get_child_by_name('attributes-list')
            policy_info = attributes_list.get_child_by_name(
                'net-firewall-policy-info')
            ips = self.na_helper.get_value_for_list(
                from_zapi=True,
                zapi_parent=policy_info.get_child_by_name('allow-list'))
            return {'service': policy_info['service'], 'allow_list': ips}
        return None

    def create_firewall_policy(self):
        """
        Create a firewall policy for given vserver
        :return: None
        """
        net_firewall_policy_obj = netapp_utils.zapi.NaElement(
            "net-firewall-policy-create")
        net_firewall_policy_obj.translate_struct(
            self.firewall_policy_attributes())
        if self.parameters.get('allow_list'):
            self.validate_ip_addresses()
            net_firewall_policy_obj.add_child_elem(
                self.na_helper.get_value_for_list(
                    from_zapi=False,
                    zapi_parent='allow-list',
                    zapi_child='ip-and-mask',
                    data=self.parameters['allow_list']))
        try:
            self.server.invoke_successfully(net_firewall_policy_obj,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error creating Firewall Policy: %s" %
                                  (to_native(error)),
                                  exception=traceback.format_exc())

    def destroy_firewall_policy(self):
        """
        Destroy a Firewall Policy from a vserver
        :return: None
        """
        net_firewall_policy_obj = netapp_utils.zapi.NaElement(
            "net-firewall-policy-destroy")
        net_firewall_policy_obj.translate_struct(
            self.firewall_policy_attributes())
        try:
            self.server.invoke_successfully(net_firewall_policy_obj,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error destroying Firewall Policy: %s" %
                                  (to_native(error)),
                                  exception=traceback.format_exc())

    def modify_firewall_policy(self, modify):
        """
        Modify a firewall Policy on a vserver
        :return: none
        """
        self.validate_ip_addresses()
        net_firewall_policy_obj = netapp_utils.zapi.NaElement(
            "net-firewall-policy-modify")
        net_firewall_policy_obj.translate_struct(
            self.firewall_policy_attributes())
        net_firewall_policy_obj.add_child_elem(
            self.na_helper.get_value_for_list(from_zapi=False,
                                              zapi_parent='allow-list',
                                              zapi_child='ip-and-mask',
                                              data=modify['allow_list']))
        try:
            self.server.invoke_successfully(net_firewall_policy_obj,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error modifying Firewall Policy: %s" %
                                  (to_native(error)),
                                  exception=traceback.format_exc())

    def firewall_policy_attributes(self):
        return {
            'policy': self.parameters['policy'],
            'service': self.parameters['service'],
            'vserver': self.parameters['vserver'],
        }

    def get_firewall_config_for_node(self):
        """
        Get firewall configuration on the node
        :return: dict() with firewall config details
        """
        if self.parameters.get('logging'):
            if self.parameters.get('node') is None:
                self.module.fail_json(
                    msg=
                    'Error: Missing parameter \'node\' to modify firewall logging'
                )
        net_firewall_config_obj = netapp_utils.zapi.NaElement(
            "net-firewall-config-get")
        net_firewall_config_obj.add_new_child('node-name',
                                              self.parameters['node'])
        try:
            result = self.server.invoke_successfully(net_firewall_config_obj,
                                                     True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg="Error getting Firewall Configuration: %s" %
                (to_native(error)),
                exception=traceback.format_exc())
        if result.get_child_by_name('attributes'):
            firewall_info = result['attributes'].get_child_by_name(
                'net-firewall-config-info')
            return {
                'enable':
                self.change_status_to_bool(
                    firewall_info.get_child_content('is-enabled'),
                    to_zapi=False),
                'logging':
                self.change_status_to_bool(
                    firewall_info.get_child_content('is-logging'),
                    to_zapi=False)
            }
        return None

    def modify_firewall_config(self, modify):
        """
        Modify the configuration of a firewall on node
        :return: None
        """
        net_firewall_config_obj = netapp_utils.zapi.NaElement(
            "net-firewall-config-modify")
        net_firewall_config_obj.add_new_child('node-name',
                                              self.parameters['node'])
        if modify.get('enable'):
            net_firewall_config_obj.add_new_child(
                'is-enabled',
                self.change_status_to_bool(self.parameters['enable']))
        if modify.get('logging'):
            net_firewall_config_obj.add_new_child(
                'is-logging',
                self.change_status_to_bool(self.parameters['logging']))
        try:
            self.server.invoke_successfully(net_firewall_config_obj,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error modifying Firewall Config: %s" %
                                  (to_native(error)),
                                  exception=traceback.format_exc())

    def change_status_to_bool(self, input, to_zapi=True):
        if to_zapi:
            return 'true' if input == 'enable' else 'false'
        else:
            return 'enable' if input == 'true' else 'disable'

    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_firewall_policy", cserver)

    def apply(self):
        self.autosupport_log()
        cd_action, modify, modify_config = None, None, None
        if self.parameters.get('policy'):
            current = self.get_firewall_policy()
            cd_action = self.na_helper.get_cd_action(current, self.parameters)
            if cd_action is None and self.parameters['state'] == 'present':
                modify = self.na_helper.get_modified_attributes(
                    current, self.parameters)
        if self.parameters.get('node'):
            current_config = self.get_firewall_config_for_node()
            # firewall config for a node is always present, we cannot create or delete a firewall on a node
            modify_config = self.na_helper.get_modified_attributes(
                current_config, self.parameters)

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_firewall_policy()
                elif cd_action == 'delete':
                    self.destroy_firewall_policy()
                else:
                    if modify:
                        self.modify_firewall_policy(modify)
                    if modify_config:
                        self.modify_firewall_config(modify_config)
        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)
Exemple #13
0
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)
Exemple #14
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)
class NetAppOntapSVM(object):
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 name=dict(required=True, type='str'),
                 from_name=dict(required=False, type='str'),
                 root_volume=dict(type='str'),
                 root_volume_aggregate=dict(type='str'),
                 root_volume_security_style=dict(
                     type='str', choices=['unix', 'ntfs', 'mixed', 'unified']),
                 allowed_protocols=dict(type='list'),
                 aggr_list=dict(type='list'),
                 ipspace=dict(type='str', required=False),
                 snapshot_policy=dict(type='str', required=False),
                 language=dict(type='str', required=False),
                 subtype=dict(choices=[
                     'default', 'dp_destination', 'sync_source',
                     'sync_destination'
                 ]),
                 comment=dict(type="str", required=False)))

        self.module = AnsibleModule(argument_spec=self.argument_spec,
                                    supports_check_mode=True)
        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)

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

    def get_vserver(self, vserver_name=None):
        """
        Checks if vserver exists.

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

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

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

        result = self.server.invoke_successfully(vserver_info,
                                                 enable_tunneling=False)
        vserver_details = None
        if (result.get_child_by_name('num-records')
                and int(result.get_child_content('num-records')) >= 1):
            attributes_list = result.get_child_by_name('attributes-list')
            vserver_info = attributes_list.get_child_by_name('vserver-info')
            aggr_list = list()
            ''' vserver aggr-list can be empty by default'''
            get_list = vserver_info.get_child_by_name('aggr-list')
            if get_list is not None:
                aggregates = get_list.get_children()
                for aggr in aggregates:
                    aggr_list.append(aggr.get_content())

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

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

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

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

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

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

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

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

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

    def asup_log_for_cserver(self, event_name):
        """
        Fetch admin vserver for the given cluster
        Create and Autosupport log event with the given module name
        :param event_name: Name of the event log
        :return: None
        """
        results = netapp_utils.get_cserver(self.server)
        cserver = netapp_utils.setup_na_ontap_zapi(module=self.module,
                                                   vserver=results)
        netapp_utils.ems_log_event(event_name, cserver)
Exemple #16
0
class ElementSWVolumePair(object):
    ''' class to handle volume pairing operations '''

    def __init__(self):
        """
            Setup Ansible parameters and SolidFire 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'),
            src_volume=dict(required=True, type='str'),
            src_account=dict(required=True, type='str'),
            dest_volume=dict(required=True, type='str'),
            dest_account=dict(required=True, type='str'),
            mode=dict(required=False, type='str',
                      choices=['async', 'sync', 'snapshotsonly'],
                      default='async'),
            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, vol_id):
        """
            Check for idempotency
            A volume can have only one pair
            Return paired-volume-id if volume is paired already
            None if volume is not paired
        """
        paired_volumes = self.elem.list_volumes(volume_ids=[vol_id],
                                                is_paired=True)
        for vol in paired_volumes.volumes:
            for pair in vol.volume_pairs:
                if pair is not None:
                    return pair.remote_volume_id
        return None

    def pair_volumes(self):
        """
            Start volume pairing on source, and complete on target volume
        """
        try:
            pair_key = self.elem.start_volume_pairing(
                volume_id=self.parameters['src_vol_id'],
                mode=self.parameters['mode'])
            self.dest_elem.complete_volume_pairing(
                volume_pairing_key=pair_key.volume_pairing_key,
                volume_id=self.parameters['dest_vol_id'])
        except solidfire.common.ApiServerError as err:
            self.module.fail_json(msg="Error pairing volume id %s"
                                      % (self.parameters['src_vol_id']),
                                  exception=to_native(err))

    def pairing_exists(self, src_id, dest_id):
        src_paired = self.check_if_already_paired(self.parameters['src_vol_id'])
        dest_paired = self.check_if_already_paired(self.parameters['dest_vol_id'])
        if src_paired is not None or dest_paired is not None:
            return True
        return None

    def unpair_volumes(self):
        """
            Delete volume pair
        """
        try:
            self.elem.remove_volume_pair(volume_id=self.parameters['src_vol_id'])
            self.dest_elem.remove_volume_pair(volume_id=self.parameters['dest_vol_id'])
        except solidfire.common.ApiServerError as err:
            self.module.fail_json(msg="Error unpairing volume ids %s and %s"
                                      % (self.parameters['src_vol_id'],
                                         self.parameters['dest_vol_id']),
                                  exception=to_native(err))

    def get_account_id(self, account, type):
        """
            Get source and destination account IDs
        """
        try:
            if type == 'src':
                self.parameters['src_account_id'] = self.elementsw_helper.account_exists(account)
            elif type == 'dest':
                self.parameters['dest_account_id'] = self.dest_elementsw_helper.account_exists(account)
        except solidfire.common.ApiServerError as err:
            self.module.fail_json(msg="Error: either account %s or %s does not exist"
                                      % (self.parameters['src_account'],
                                         self.parameters['dest_account']),
                                  exception=to_native(err))

    def get_volume_id(self, volume, type):
        """
            Get source and destination volume IDs
        """
        if type == 'src':
            self.parameters['src_vol_id'] = self.elementsw_helper.volume_exists(volume, self.parameters['src_account_id'])
            if self.parameters['src_vol_id'] is None:
                self.module.fail_json(msg="Error: source volume %s does not exist"
                                          % (self.parameters['src_volume']))
        elif type == 'dest':
            self.parameters['dest_vol_id'] = self.dest_elementsw_helper.volume_exists(volume, self.parameters['dest_account_id'])
            if self.parameters['dest_vol_id'] is None:
                self.module.fail_json(msg="Error: destination volume %s does not exist"
                                      % (self.parameters['dest_volume']))

    def get_ids(self):
        """
            Get IDs for volumes and accounts
        """
        self.get_account_id(self.parameters['src_account'], 'src')
        self.get_account_id(self.parameters['dest_account'], 'dest')
        self.get_volume_id(self.parameters['src_volume'], 'src')
        self.get_volume_id(self.parameters['dest_volume'], 'dest')

    def apply(self):
        """
            Call create / delete volume pair methods
        """
        self.get_ids()
        paired = self.pairing_exists(self.parameters['src_vol_id'],
                                     self.parameters['dest_vol_id'])
        # calling helper to determine action
        cd_action = self.na_helper.get_cd_action(paired, self.parameters)
        if cd_action == "create":
            self.pair_volumes()
        elif cd_action == "delete":
            self.unpair_volumes()
        self.module.exit_json(changed=self.na_helper.changed)
Exemple #17
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)
Exemple #18
0
class NetAppOntapUnixUser(object):
    """
    Common operations to manage users and roles.
    """
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(
                state=dict(required=False,
                           choices=['present', 'absent'],
                           default='present'),
                name=dict(required=True, type='str'),
                group_id=dict(required=False, type='int'),
                id=dict(required=False, type='int'),
                full_name=dict(required=False, type='str'),
                vserver=dict(required=True, type='str'),
            ))

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

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

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

    def get_unix_user(self):
        """
        Checks if the UNIX user exists.

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

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

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

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

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

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

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

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

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

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

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

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

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

        :return: None
        """
        self.autosupport_log()
        current = self.get_unix_user()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if self.parameters['state'] == 'present' and cd_action is None:
            modify = self.na_helper.get_modified_attributes(
                current, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_unix_user()
                elif cd_action == 'delete':
                    self.delete_unix_user()
                else:
                    self.modify_unix_user(modify)
        self.module.exit_json(changed=self.na_helper.changed)
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)
Exemple #20
0
class NetAppOntapSubnet(object):
    """
    Create, Modifies and Destroys a subnet
    """
    def __init__(self):
        """
        Initialize the ONTAP Subnet class
        """
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 name=dict(required=True, type='str'),
                 from_name=dict(required=False, type='str'),
                 broadcast_domain=dict(required=False, type='str'),
                 gateway=dict(required=False, type='str'),
                 ip_ranges=dict(required=False, type=list),
                 ipspace=dict(required=False, type='str'),
                 subnet=dict(required=False, type='str')))

        self.module = AnsibleModule(argument_spec=self.argument_spec,
                                    supports_check_mode=True)
        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)

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

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

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

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

        subnet_iter.add_child_elem(query)

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

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

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

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

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

        return return_value

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

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

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

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

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

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

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

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

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

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

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

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

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

    def apply(self):
        '''Apply action to subnet'''
        results = netapp_utils.get_cserver(self.server)
        cserver = netapp_utils.setup_na_ontap_zapi(module=self.module,
                                                   vserver=results)
        netapp_utils.ems_log_event("na_ontap_net_subnet", cserver)
        current = self.get_subnet()
        cd_action, rename = None, None

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

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

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if rename:
                    self.rename_subnet()
                # If rename is True, cd_action is NOne but modify could be true
                if cd_action == 'create':
                    for attribute in ['subnet', 'broadcast_domain']:
                        if not self.parameters.get(attribute):
                            self.module.fail_json(
                                msg='Error - missing required arguments: %s.' %
                                attribute)
                    self.create_subnet()
                elif cd_action == 'delete':
                    self.delete_subnet()
                elif modify:
                    self.modify_subnet()
        self.module.exit_json(changed=self.na_helper.changed)
Exemple #21
0
class NetAppOntapUserRole(object):
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 name=dict(required=True, type='str'),
                 command_directory_name=dict(required=True, type='str'),
                 access_level=dict(required=False,
                                   type='str',
                                   default='all',
                                   choices=['none', 'readonly', 'all']),
                 vserver=dict(required=True, type='str'),
                 query=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['vserver'])

    def get_role(self):
        """
        Checks if the role exists for specific command-directory-name.

        :return:
            True if role found
            False if role is not found
        :rtype: bool
        """
        options = {
            'vserver': self.parameters['vserver'],
            'role-name': self.parameters['name'],
            'command-directory-name': self.parameters['command_directory_name']
        }

        security_login_role_get_iter = netapp_utils.zapi.NaElement(
            'security-login-role-get-iter')
        query_details = netapp_utils.zapi.NaElement.create_node_with_children(
            'security-login-role-info', **options)
        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(query_details)
        security_login_role_get_iter.add_child_elem(query)

        try:
            result = self.server.invoke_successfully(
                security_login_role_get_iter, enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as e:
            # Error 16031 denotes a role not being found.
            if to_native(e.code) == "16031":
                return None
            # Error 16039 denotes command directory not found.
            elif to_native(e.code) == "16039":
                return None
            else:
                self.module.fail_json(msg='Error getting role %s: %s' %
                                      (self.name, to_native(e)),
                                      exception=traceback.format_exc())
        if (result.get_child_by_name('num-records')
                and int(result.get_child_content('num-records')) >= 1):
            role_info = result.get_child_by_name(
                'attributes-list').get_child_by_name(
                    'security-login-role-info')
            result = {
                'name': role_info['role-name'],
                'access_level': role_info['access-level'],
                'command_directory_name': role_info['command-directory-name'],
                'query': role_info['role-query']
            }
            return result

        return None

    def create_role(self):
        options = {
            'vserver': self.parameters['vserver'],
            'role-name': self.parameters['name'],
            'command-directory-name':
            self.parameters['command_directory_name'],
            'access-level': self.parameters['access_level']
        }
        if self.parameters.get('query'):
            options['role-query'] = self.parameters['query']
        role_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'security-login-role-create', **options)

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

    def delete_role(self):
        role_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'security-login-role-delete', **{
                'vserver': self.parameters['vserver'],
                'role-name': self.parameters['name'],
                'command-directory-name':
                self.parameters['command_directory_name']
            })

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

    def modify_role(self, modify):
        options = {
            'vserver': self.parameters['vserver'],
            'role-name': self.parameters['name'],
            'command-directory-name': self.parameters['command_directory_name']
        }
        if 'access_level' in modify.keys():
            options['access-level'] = self.parameters['access_level']
        if 'query' in modify.keys():
            options['role-query'] = self.parameters['query']

        role_modify = netapp_utils.zapi.NaElement.create_node_with_children(
            'security-login-role-modify', **options)

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

    def apply(self):
        self.asup_log_for_cserver('na_ontap_user_role')
        current = self.get_role()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)

        # if desired state specify empty quote query and current query is None, set desired query to None.
        # otherwise na_helper.get_modified_attributes will detect a change.
        if self.parameters.get('query') == '' and current is not None:
            if current['query'] is None:
                self.parameters['query'] = 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_role()
                elif cd_action == 'delete':
                    self.delete_role()
                elif modify:
                    self.modify_role(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
        """
        netapp_utils.ems_log_event(event_name, self.server)
Exemple #22
0
class NetAppOntapVscanOnDemandTask(object):
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(choices=['present', 'absent'], default='present'),
                 vserver=dict(required=True, type='str'),
                 cross_junction=dict(required=False,
                                     type='bool',
                                     default=False),
                 directory_recursion=dict(required=False,
                                          type='bool',
                                          default=False),
                 file_ext_to_exclude=dict(required=False, type="list"),
                 file_ext_to_include=dict(required=False, type="list"),
                 max_file_size=dict(required=False, type="str"),
                 paths_to_exclude=dict(required=False, type="list"),
                 report_directory=dict(required=False, type='str'),
                 report_log_level=dict(required=False,
                                       choices=['verbose', 'info', 'error'],
                                       default='error'),
                 request_timeout=dict(required=False, type='str'),
                 scan_files_with_no_ext=dict(required=False,
                                             type='bool',
                                             default=True),
                 scan_paths=dict(required=False, type="list"),
                 scan_priority=dict(required=False,
                                    choices=['low', 'normal'],
                                    default='low'),
                 schedule=dict(required=False, type="str"),
                 task_name=dict(required=True, type="str")))
        self.module = AnsibleModule(argument_spec=self.argument_spec,
                                    supports_check_mode=True,
                                    required_if=[[
                                        "state", "present",
                                        ["report_directory", "scan_paths"]
                                    ]])
        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_demand_task(self):
        """
        Get a demand task
        :return: A vscan-on-demand-task-info or None
        """
        demand_task_iter = netapp_utils.zapi.NaElement(
            "vscan-on-demand-task-get-iter")
        demand_task_info = netapp_utils.zapi.NaElement(
            "vscan-on-demand-task-info")
        demand_task_info.add_new_child('task-name',
                                       self.parameters['task_name'])
        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(demand_task_info)
        demand_task_iter.add_child_elem(query)
        try:
            result = self.server.invoke_successfully(demand_task_iter, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error searching for Vscan on demand task %s: %s' %
                (self.parameters['task_name'], to_native(error)),
                exception=traceback.format_exc())
        if result.get_child_by_name('num-records') and int(
                result.get_child_content('num-records')) >= 1:
            return result.get_child_by_name(
                'attributes-list').get_child_by_name(
                    'vscan-on-demand-task-info')
        return None

    def create_demand_task(self):
        """
        Create a Demand Task
        :return: None
        """
        demand_task_obj = netapp_utils.zapi.NaElement(
            "vscan-on-demand-task-create")
        # Required items first
        demand_task_obj.add_new_child('report-directory',
                                      self.parameters['report_directory'])
        demand_task_obj.add_new_child('task-name',
                                      self.parameters['task_name'])
        scan_paths = netapp_utils.zapi.NaElement("scan-paths")
        for scan_path in self.parameters['scan_paths']:
            scan_paths.add_new_child('string', scan_path)
        demand_task_obj.add_child_elem(scan_paths)
        # Optional items next
        if self.parameters.get('cross_junction'):
            demand_task_obj.add_new_child(
                'cross-junction',
                str(self.parameters['cross_junction']).lower())
        if self.parameters.get('directory_recursion'):
            demand_task_obj.add_new_child(
                'directory-recursion',
                str(self.parameters['directory_recursion']).lower())
        if self.parameters.get('file_ext_to_exclude'):
            ext_to_exclude_obj = netapp_utils.zapi.NaElement(
                'file-ext-to-exclude')
            for exclude_file in self.parameters['file_ext_to_exclude']:
                ext_to_exclude_obj.add_new_child('file-extension',
                                                 exclude_file)
            demand_task_obj.add_child_elem(ext_to_exclude_obj)
        if self.parameters.get('file_ext_to_include'):
            ext_to_include_obj = netapp_utils.zapi.NaElement(
                'file-ext-to-include')
            for include_file in self.parameters['file_ext_to_exclude']:
                ext_to_include_obj.add_child_elem('file-extension',
                                                  include_file)
            demand_task_obj.add_child_elem(ext_to_include_obj)
        if self.parameters.get('max_file_size'):
            demand_task_obj.add_new_child('max-file-size',
                                          self.parameters['max_file_size'])
        if self.parameters.get('paths_to_exclude'):
            exclude_paths = netapp_utils.zapi.NaElement('paths-to-exclude')
            for path in self.parameters['paths_to_exclude']:
                exclude_paths.add_new_child('string', path)
            demand_task_obj.add_child_elem(exclude_paths)
        if self.parameters.get('report_log_level'):
            demand_task_obj.add_new_child('report-log-level',
                                          self.parameters['report_log_level'])
        if self.parameters.get('request_timeout'):
            demand_task_obj.add_new_child('request-timeout',
                                          self.parameters['request_timeout'])
        if self.parameters.get('scan_files_with_no_ext'):
            demand_task_obj.add_new_child(
                'scan-files-with-no-ext',
                str(self.parameters['scan_files_with_no_ext']).lower())
        if self.parameters.get('scan_priority'):
            demand_task_obj.add_new_child(
                'scan-priority', self.parameters['scan_priority'].lower())
        if self.parameters.get('schedule'):
            demand_task_obj.add_new_child('schedule',
                                          self.parameters['schedule'])
        try:
            result = self.server.invoke_successfully(demand_task_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error creating on demand task %s: %s' %
                (self.parameters['task_name'], to_native(error)),
                exception=traceback.format_exc())

    def delete_demand_task(self):
        """
        Delete a Demand Task"
        :return:
        """
        demand_task_obj = netapp_utils.zapi.NaElement(
            'vscan-on-demand-task-delete')
        demand_task_obj.add_new_child('task-name',
                                      self.parameters['task_name'])
        try:
            self.server.invoke_successfully(demand_task_obj, True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error deleting on demand task, %s: %s' %
                (self.parameters['task_name'], to_native(error)),
                exception=traceback.format_exc())

    def asup_log_for_cserver(self, event_name):
        """
        Fetch admin vserver for the given cluster
        Create and Autosupport log event with the given module name
        :param event_name: Name of the event log
        :return: None
        """
        results = netapp_utils.get_cserver(self.server)
        cserver = netapp_utils.setup_na_ontap_zapi(module=self.module,
                                                   vserver=results)
        netapp_utils.ems_log_event(event_name, cserver)

    def apply(self):
        self.asup_log_for_cserver("na_ontap_vscan_on_demand_task")
        current = self.get_demand_task()
        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_demand_task()
                elif cd_action == 'delete':
                    self.delete_demand_task()
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppONTAPJob(object):
    '''Class with job schedule cron methods'''
    def __init__(self):

        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 name=dict(required=True, type='str'),
                 job_minutes=dict(required=False, type='list'),
                 job_months=dict(required=False, type='list'),
                 job_hours=dict(required=False, type='list'),
                 job_days_of_month=dict(required=False, type='list'),
                 job_days_of_week=dict(required=False, type='list')))

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

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

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

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

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

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

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

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

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

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

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

    def apply(self):
        """
        Apply action to job-schedule
        """
        self.autosupport_log()
        current = self.get_job_schedule()
        action = self.na_helper.get_cd_action(current, self.parameters)
        if action is None and self.parameters['state'] == 'present':
            modify = self.na_helper.get_modified_attributes(
                current, self.parameters)

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if action == 'create':
                    self.create_job_schedule()
                elif action == 'delete':
                    self.delete_job_schedule()
                elif modify:
                    self.modify_job_schedule(modify)
        self.module.exit_json(changed=self.na_helper.changed)
Exemple #24
0
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 NetAppAWSCVS(object):
    '''Class  for  Pool operations '''

    def __init__(self):
        """
        Parse arguments, setup state variables,
        """
        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'),
            serviceLevel=dict(required=False, choices=['basic', 'standard', 'extreme'], type='str'),
            sizeInBytes=dict(required=False, type='int'),
            vendorID=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)
        self.restApi = AwsCvsRestAPI(self.module)
        self.sizeInBytes_min_value = 4000000000000

    def get_aws_netapp_cvs_pool(self, name=None):
        """
        Returns Pool object if exists else Return None
        """
        pool_info = None

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

        pools, error = self.restApi.get('Pools')

        if error is None and pools is not None:
            for pool in pools:
                if 'name' in pool and pool['region'] == self.parameters['region']:
                    if pool['name'] == name:
                        pool_info = pool
                        break

        return pool_info

    def create_aws_netapp_cvs_pool(self):
        """
        Create a pool
        """
        api = 'Pools'

        for key in ['serviceLevel', 'sizeInBytes', 'vendorID']:
            if key not in self.parameters.keys() or self.parameters[key] is None:
                self.module.fail_json(changed=False, msg="Mandatory key '%s' required" % (key))

        pool = {
            "name": self.parameters['name'],
            "region": self.parameters['region'],
            "serviceLevel": self.parameters['serviceLevel'],
            "sizeInBytes": self.parameters['sizeInBytes'],
            "vendorID": self.parameters['vendorID']
        }

        response, error = self.restApi.post(api, pool)
        if error is not None:
            self.module.fail_json(changed=False, msg=error)

    def update_aws_netapp_cvs_pool(self, update_pool_info, pool_id):
        """
        Update a pool
        """
        api = 'Pools/' + pool_id

        pool = {
            "name": update_pool_info['name'],
            "region": self.parameters['region'],
            "serviceLevel": update_pool_info['serviceLevel'],
            "sizeInBytes": update_pool_info['sizeInBytes'],
            "vendorID": update_pool_info['vendorID']
        }

        response, error = self.restApi.put(api, pool)
        if error is not None:
            self.module.fail_json(changed=False, msg=error)

    def delete_aws_netapp_cvs_pool(self, pool_id):
        """
        Delete a pool
        """
        api = 'Pools/' + pool_id
        data = None
        response, error = self.restApi.delete(api, data)

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

    def apply(self):
        """
        Perform pre-checks, call functions and exit
        """
        update_required = False
        cd_action = None

        if 'sizeInBytes' in self.parameters.keys() and self.parameters['sizeInBytes'] < self.sizeInBytes_min_value:
            self.module.fail_json(changed=False, msg="sizeInBytes should be greater than  or equal to %d" % (self.sizeInBytes_min_value))

        current = self.get_aws_netapp_cvs_pool()
        if self.parameters.get('from_name'):
            existing = self.get_aws_netapp_cvs_pool(self.parameters['from_name'])
            rename = self.na_helper.is_rename_action(existing, current)
            if rename is None:
                self.module.fail_json(changed=False, msg="unable to rename pool: '%s' does not exist" % self.parameters['from_name'])
            if rename:
                current = existing
        else:
            cd_action = self.na_helper.get_cd_action(current, self.parameters)

        if cd_action is None and self.parameters['state'] == 'present':
            keys_to_check = ['name', 'vendorID', 'sizeInBytes', 'serviceLevel']
            update_pool_info, update_required = self.na_helper.compare_and_update_values(current, self.parameters, keys_to_check)

            if update_required is True:
                self.na_helper.changed = True
                cd_action = 'update'

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'update':
                    self.update_aws_netapp_cvs_pool(update_pool_info, current['poolId'])
                elif cd_action == 'create':
                    self.create_aws_netapp_cvs_pool()
                elif cd_action == 'delete':
                    self.delete_aws_netapp_cvs_pool(current['poolId'])

        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 NetAppOntapUnixGroup(object):
    """
    Common operations to manage UNIX groups
    """
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 name=dict(required=True, type='str'),
                 id=dict(required=False, type='int'),
                 skip_name_validation=dict(required=False, type='bool'),
                 vserver=dict(required=True, type='str'),
                 users=dict(required=False, type='list')))

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        :return: None
        """
        self.autosupport_log()
        current = self.get_unix_group()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if self.parameters['state'] == 'present' and cd_action is None:
            modify = self.na_helper.get_modified_attributes(
                current, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_unix_group()
                elif cd_action == 'delete':
                    self.delete_unix_group()
                else:
                    self.modify_unix_group(modify)
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapUser(object):
    """
    Common operations to manage users and roles.
    """
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(
                state=dict(required=False,
                           choices=['present', 'absent'],
                           default='present'),
                name=dict(required=True, type='str'),
                applications=dict(
                    required=True,
                    type='list',
                    aliases=['application'],
                    choices=[
                        'console', 'http', 'ontapi', 'rsh', 'snmp', 'sp',
                        'service-processor', 'ssh', 'telnet'
                    ],
                ),
                authentication_method=dict(required=True,
                                           type='str',
                                           choices=[
                                               'community', 'password',
                                               'publickey', 'domain',
                                               'nsswitch', 'usm', 'cert'
                                           ]),
                set_password=dict(required=False, type='str', no_log=True),
                role_name=dict(required=False, type='str'),
                lock_user=dict(required=False, type='bool'),
                vserver=dict(required=True, type='str'),
            ))

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self.server.set_vserver(None)
        return True

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

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

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

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

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

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

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

        if self.na_helper.changed:

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

                if lock_user:
                    if self.parameters.get('lock_user'):
                        self.lock_given_user()
                    else:
                        self.unlock_given_user()
                if not create_delete_decision and self.parameters.get(
                        'set_password') is not None:
                    # if change password return false nothing has changed so we need to set changed to False
                    self.na_helper.changed = self.change_password()
        self.module.exit_json(changed=self.na_helper.changed)
Exemple #29
0
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)
Exemple #30
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)