Ejemplo n.º 1
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)
class NetAppOntapQosPolicyGroup(object):
    """
    Create, delete, modify and rename a policy group.
    """
    def __init__(self):
        """
        Initialize the Ontap qos policy group class.
        """
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            type='str',
                            choices=['present', 'absent'],
                            default='present'),
                 name=dict(required=True, type='str'),
                 from_name=dict(required=False, type='str'),
                 vserver=dict(required=True, type='str'),
                 max_throughput=dict(required=False, type='str'),
                 min_throughput=dict(required=False, type='str'),
                 force=dict(required=False, type='bool', default=False)))

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

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

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

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

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

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

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

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

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

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

    def apply(self):
        """
        Run module based on playbook
        """
        self.asup_log_for_cserver("na_ontap_qos_policy_group")
        current = self.get_policy_group()
        rename, cd_action = None, None
        if self.parameters.get('from_name'):
            rename = self.na_helper.is_rename_action(
                self.get_policy_group(self.parameters['from_name']), current)
        else:
            cd_action = self.na_helper.get_cd_action(current, self.parameters)
        modify = self.na_helper.get_modified_attributes(
            current, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if rename:
                    self.rename_policy_group()
                if cd_action == 'create':
                    self.create_policy_group()
                elif cd_action == 'delete':
                    self.delete_policy_group()
                elif modify:
                    self.modify_helper(modify)
        self.module.exit_json(changed=self.na_helper.changed)

    def asup_log_for_cserver(self, event_name):
        """
        Fetch admin vserver for the given cluster
        Create and Autosupport log event with the given module name
        :param event_name: Name of the event log
        :return: None
        """
        results = netapp_utils.get_cserver(self.server)
        cserver = netapp_utils.setup_na_ontap_zapi(module=self.module,
                                                   vserver=results)
        netapp_utils.ems_log_event(event_name, cserver)
Ejemplo n.º 3
0
class NetAppOntapSnapshot(object):
    """
    Creates, modifies, and deletes a Snapshot
    """
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(
                state=dict(required=False,
                           choices=['present', 'absent'],
                           default='present'),
                from_name=dict(required=False, type='str'),
                snapshot=dict(required=True, type="str"),
                volume=dict(required=True, type="str"),
                async_bool=dict(required=False, type="bool", default=False),
                comment=dict(required=False, type="str"),
                snapmirror_label=dict(required=False, type="str"),
                ignore_owners=dict(required=False, type="bool", default=False),
                snapshot_instance_uuid=dict(required=False, type="str"),
                vserver=dict(required=True, type="str"),
            ))
        self.module = AnsibleModule(argument_spec=self.argument_spec,
                                    supports_check_mode=True)

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

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

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

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

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

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

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

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

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

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

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

    def apply(self):
        """
        Check to see which play we should run
        """
        current = self.get_snapshot()
        netapp_utils.ems_log_event("na_ontap_snapshot", self.server)
        rename, cd_action = None, None
        modify = {}
        if self.parameters.get('from_name'):
            current_old_name = self.get_snapshot(self.parameters['from_name'])
            rename = self.na_helper.is_rename_action(current_old_name, current)
            modify = self.na_helper.get_modified_attributes(
                current_old_name, self.parameters)
        else:
            cd_action = self.na_helper.get_cd_action(current, self.parameters)
            if cd_action is None:
                modify = self.na_helper.get_modified_attributes(
                    current, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if rename:
                    self.rename_snapshot()
                if cd_action == 'create':
                    self.create_snapshot()
                elif cd_action == 'delete':
                    self.delete_snapshot()
                elif modify:
                    self.modify_snapshot()
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapIgroupInitiator(object):
    def __init__(self):

        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(
                state=dict(required=False,
                           choices=['present', 'absent'],
                           default='present'),
                names=dict(required=True, type='list', aliases=['name']),
                initiator_group=dict(required=True, type='str'),
                vserver=dict(required=True, type='str'),
            ))

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

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

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

    def get_initiators(self):
        """
        Get the existing list of initiators from an igroup
        :rtype: list() or None
        """
        igroup_info = netapp_utils.zapi.NaElement('igroup-get-iter')
        attributes = dict(
            query={
                'initiator-group-info': {
                    'initiator-group-name': self.parameters['initiator_group'],
                    'vserver': self.parameters['vserver']
                }
            })
        igroup_info.translate_struct(attributes)
        result, current = None, []

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

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

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

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

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

    def apply(self):
        self.autosupport_log()
        initiators = self.get_initiators()
        for initiator in self.parameters['names']:
            present = None
            if initiator in initiators:
                present = True
            cd_action = self.na_helper.get_cd_action(present, self.parameters)
            if self.na_helper.changed:
                if self.module.check_mode:
                    pass
                else:
                    if cd_action == 'create':
                        self.modify_initiator(initiator, 'igroup-add')
                    elif cd_action == 'delete':
                        self.modify_initiator(initiator, 'igroup-remove')
        self.module.exit_json(changed=self.na_helper.changed)
Ejemplo n.º 5
0
class AwsCvsNetappFileSystem(object):
    """
    Contains methods to parse arguments,
    derive details of AWS_CVS objects
    and send requests to AWS CVS via
    the restApi
    """
    def __init__(self):
        """
        Parse arguments, setup state variables,
        check parameters and ensure request module is installed
        """
        self.argument_spec = netapp_utils.aws_cvs_host_argument_spec()
        self.argument_spec.update(
            dict(
                state=dict(required=True, choices=['present', 'absent']),
                region=dict(required=True, type='str'),
                creationToken=dict(required=True, type='str'),
                quotaInBytes=dict(required=False, type='int'),
                serviceLevel=dict(required=False,
                                  choices=['standard', 'premium', 'extreme']),
                exportPolicy=dict(
                    type='dict',
                    options=dict(rules=dict(
                        type='list',
                        options=dict(
                            allowedClients=dict(required=False, type='str'),
                            cifs=dict(required=False, type='bool'),
                            nfsv3=dict(required=False, type='bool'),
                            nfsv4=dict(required=False, type='bool'),
                            ruleIndex=dict(required=False, type='int'),
                            unixReadOnly=dict(required=False, type='bool'),
                            unixReadWrite=dict(required=False,
                                               type='bool'))))),
            ))

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

        self.na_helper = NetAppModule()

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

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

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

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

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

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

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

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

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

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

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

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

        fileSystem = None
        fileSystemId = self.get_filesystemId()

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

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

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

            if update_fileSystem:
                self.na_helper.changed = True

        result_message = ""

        if self.na_helper.changed:
            if self.module.check_mode:
                # Skip changes
                result_message = "Check mode, skipping changes"
            else:
                if cd_action == "create":
                    self.create_fileSystem()
                    result_message = "FileSystem Created"
                elif cd_action == "delete":
                    self.delete_fileSystem(fileSystemId)
                    result_message = "FileSystem Deleted"
                else:  # modify
                    self.update_fileSystem(fileSystemId)
                    result_message = "FileSystem Updated"
        self.module.exit_json(changed=self.na_helper.changed,
                              msg=result_message)
Ejemplo n.º 6
0
class NetAppONTAPNVMENamespace(object):
    """
    Class with NVME namespace methods
    """
    def __init__(self):

        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            type='str',
                            choices=['present', 'absent'],
                            default='present'),
                 vserver=dict(required=True, type='str'),
                 ostype=dict(
                     required=False,
                     type='str',
                     choices=['windows', 'linux', 'vmware', 'xen', 'hyper_v']),
                 path=dict(required=True, type='str'),
                 size=dict(required=False, type='int')))

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

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

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

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

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

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

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

        self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapInterface(object):
    ''' object to describe  interface info '''
    def __init__(self):

        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(dict(
            state=dict(required=False, choices=[
                       'present', 'absent'], default='present'),
            interface_name=dict(required=True, type='str'),
            home_node=dict(required=False, type='str', default=None),
            home_port=dict(required=False, type='str'),
            role=dict(required=False, type='str'),
            address=dict(required=False, type='str'),
            netmask=dict(required=False, type='str'),
            vserver=dict(required=True, type='str'),
            firewall_policy=dict(required=False, type='str', default=None),
            failover_policy=dict(required=False, type='str', default=None,
                                 choices=['disabled', 'system-defined',
                                          'local-only', 'sfo-partner-only', 'broadcast-domain-wide']),
            admin_status=dict(required=False, choices=['up', 'down']),
            subnet_name=dict(required=False, type='str'),
            is_auto_revert=dict(required=False, type='bool', default=None),
            protocols=dict(required=False, type='list'),
            force_subnet_association=dict(required=False, type='bool', default=None),
            dns_domain_name=dict(required=False, type='str'),
            listen_for_dns_query=dict(required=False, type='bool'),
            is_dns_update_enabled=dict(required=False, type='bool')
        ))

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

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

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

    def get_interface(self):
        """
        Return details about the interface
        :param:
            name : Name of the name of the interface

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

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

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

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

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

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

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

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

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

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

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

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

    def apply(self):
        ''' calling all interface features '''
        self.autosupport_log()
        current = self.get_interface()
        # rename and create are mutually exclusive
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        modify = self.na_helper.get_modified_attributes(current, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_interface()
                elif cd_action == 'delete':
                    self.delete_interface(current['admin_status'])
                elif modify:
                    self.modify_interface(modify)
        self.module.exit_json(changed=self.na_helper.changed)
class ElementSWVlan(object):
    """ class to handle VLAN operations """
    def __init__(self):
        """
            Setup Ansible parameters and ElementSW connection
        """
        self.argument_spec = netapp_utils.ontap_sf_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 name=dict(required=False, type='str'),
                 vlan_tag=dict(required=True, type='str'),
                 svip=dict(required=False, type='str'),
                 netmask=dict(required=False, type='str'),
                 gateway=dict(required=False, type='str'),
                 namespace=dict(required=False, type='bool'),
                 attributes=dict(required=False, type='dict'),
                 address_blocks=dict(required=False, type='list')))

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

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

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

        self.elementsw_helper = NaElementSWModule(self.elem)

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

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

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

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

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

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

    def apply(self):
        """
            Call create / delete / modify vlan methods
        """
        network = self.get_network_details()
        # calling helper to determine action
        cd_action = self.na_helper.get_cd_action(network, self.parameters)
        modify = self.na_helper.get_modified_attributes(
            network, self.parameters)
        if cd_action == "create":
            self.create_network()
        elif cd_action == "delete":
            self.delete_network()
        elif modify:
            self.modify_network(modify)
        self.module.exit_json(changed=self.na_helper.changed)
Ejemplo n.º 9
0
class NetAppONTAPClusterPeer(object):
    """
    Class with cluster 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'),
                 source_intercluster_lifs=dict(
                     required=False,
                     type='list',
                     aliases=['source_intercluster_lif']),
                 dest_intercluster_lifs=dict(required=False,
                                             type='list',
                                             aliases=['dest_intercluster_lif'
                                                      ]),
                 passphrase=dict(required=False, type='str', no_log=True),
                 dest_hostname=dict(required=True, type='str'),
                 dest_username=dict(required=False, type='str'),
                 dest_password=dict(required=False, type='str', no_log=True),
                 source_cluster_name=dict(required=False, type='str'),
                 dest_cluster_name=dict(required=False, type='str')))

        self.module = AnsibleModule(argument_spec=self.argument_spec,
                                    required_together=[[
                                        'source_intercluster_lifs',
                                        'dest_intercluster_lifs'
                                    ]],
                                    required_if=[('state', 'absent', [
                                        'source_cluster_name',
                                        'dest_cluster_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)
            # set destination server connection
            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 cluster_peer_get_iter(self, cluster):
        """
        Compose NaElement object to query current source cluster using peer-cluster-name and peer-addresses parameters
        :param cluster: type of cluster (source or destination)
        :return: NaElement object for cluster-get-iter with query
        """
        cluster_peer_get = netapp_utils.zapi.NaElement('cluster-peer-get-iter')
        query = netapp_utils.zapi.NaElement('query')
        cluster_peer_info = netapp_utils.zapi.NaElement('cluster-peer-info')
        if cluster == 'source':
            peer_lifs, peer_cluster = 'dest_intercluster_lifs', 'dest_cluster_name'
        else:
            peer_lifs, peer_cluster = 'source_intercluster_lifs', 'source_cluster_name'
        if self.parameters.get(peer_lifs):
            peer_addresses = netapp_utils.zapi.NaElement('peer-addresses')
            for peer in self.parameters.get(peer_lifs):
                peer_addresses.add_new_child('remote-inet-address', peer)
            cluster_peer_info.add_child_elem(peer_addresses)
        if self.parameters.get(peer_cluster):
            cluster_peer_info.add_new_child('cluster-name',
                                            self.parameters[peer_cluster])
        query.add_child_elem(cluster_peer_info)
        cluster_peer_get.add_child_elem(query)
        return cluster_peer_get

    def cluster_peer_get(self, cluster):
        """
        Get current cluster peer info
        :param cluster: type of cluster (source or destination)
        :return: Dictionary of current cluster peer details if query successful, else return None
        """
        cluster_peer_get_iter = self.cluster_peer_get_iter(cluster)
        result, cluster_info = None, dict()
        if cluster == 'source':
            server = self.server
        else:
            server = self.dest_server
        try:
            result = server.invoke_successfully(cluster_peer_get_iter,
                                                enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error fetching cluster peer %s: %s' %
                (self.parameters['dest_cluster_name'], to_native(error)),
                exception=traceback.format_exc())
        # return cluster peer details
        if result.get_child_by_name('num-records') and \
                int(result.get_child_content('num-records')) >= 1:
            cluster_peer_info = result.get_child_by_name(
                'attributes-list').get_child_by_name('cluster-peer-info')
            cluster_info['cluster_name'] = cluster_peer_info.get_child_content(
                'cluster-name')
            peers = cluster_peer_info.get_child_by_name('peer-addresses')
            cluster_info['peer-addresses'] = [
                peer.get_content() for peer in peers.get_children()
            ]
            return cluster_info
        return None

    def cluster_peer_delete(self, cluster):
        """
        Delete a cluster peer on source or destination
        For source cluster, peer cluster-name = destination cluster name and vice-versa
        :param cluster: type of cluster (source or destination)
        :return:
        """
        if cluster == 'source':
            server, peer_cluster_name = self.server, self.parameters[
                'dest_cluster_name']
        else:
            server, peer_cluster_name = self.dest_server, self.parameters[
                'source_cluster_name']
        cluster_peer_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'cluster-peer-delete', **{'cluster-name': peer_cluster_name})
        try:
            server.invoke_successfully(cluster_peer_delete,
                                       enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error deleting cluster peer %s: %s' %
                                  (peer_cluster_name, to_native(error)),
                                  exception=traceback.format_exc())

    def cluster_peer_create(self, cluster):
        """
        Create a cluster peer on source or destination
        For source cluster, peer addresses = destination inter-cluster LIFs and vice-versa
        :param cluster: type of cluster (source or destination)
        :return: None
        """
        cluster_peer_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'cluster-peer-create')
        if self.parameters.get('passphrase') is not None:
            cluster_peer_create.add_new_child('passphrase',
                                              self.parameters['passphrase'])
        peer_addresses = netapp_utils.zapi.NaElement('peer-addresses')
        if cluster == 'source':
            server, peer_address = self.server, self.parameters[
                'dest_intercluster_lifs']
        else:
            server, peer_address = self.dest_server, self.parameters[
                'source_intercluster_lifs']
        for each in peer_address:
            peer_addresses.add_new_child('remote-inet-address', each)
        cluster_peer_create.add_child_elem(peer_addresses)
        try:
            server.invoke_successfully(cluster_peer_create,
                                       enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error creating cluster peer %s: %s' %
                                  (peer_address, to_native(error)),
                                  exception=traceback.format_exc())

    def apply(self):
        """
        Apply action to cluster peer
        :return: None
        """
        self.asup_log_for_cserver("na_ontap_cluster_peer")
        source = self.cluster_peer_get('source')
        destination = self.cluster_peer_get('destination')
        source_action = self.na_helper.get_cd_action(source, self.parameters)
        destination_action = self.na_helper.get_cd_action(
            destination, self.parameters)
        self.na_helper.changed = False
        # create only if expected cluster peer relation is not present on both source and destination clusters
        if source_action == 'create' and destination_action == 'create':
            self.cluster_peer_create('source')
            self.cluster_peer_create('destination')
            self.na_helper.changed = True
        # delete peer relation in cluster where relation is present
        else:
            if source_action == 'delete':
                self.cluster_peer_delete('source')
                self.na_helper.changed = True
            if destination_action == 'delete':
                self.cluster_peer_delete('destination')
                self.na_helper.changed = True

        self.module.exit_json(changed=self.na_helper.changed)

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

        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'), ))
        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 modify_cluster_ha(self, configure):
        """
        Enable or disable HA on cluster
        :return: None
        """
        cluster_ha_modify = netapp_utils.zapi.NaElement.create_node_with_children(
            'cluster-ha-modify', **{'ha-configured': configure})
        try:
            self.server.invoke_successfully(cluster_ha_modify,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error modifying cluster HA to %s: %s' %
                                  (configure, to_native(error)),
                                  exception=traceback.format_exc())

    def get_cluster_ha_enabled(self):
        """
        Get current cluster HA details
        :return: dict if enabled, None if disabled
        """
        cluster_ha_get = netapp_utils.zapi.NaElement('cluster-ha-get')
        try:
            result = self.server.invoke_successfully(cluster_ha_get,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error fetching cluster HA details',
                                  exception=traceback.format_exc())
        cluster_ha_info = result.get_child_by_name(
            'attributes').get_child_by_name('cluster-ha-info')
        if cluster_ha_info.get_child_content('ha-configured') == 'true':
            return {'ha-configured': True}
        return None

    def apply(self):
        """
        Apply action to cluster HA
        """
        results = netapp_utils.get_cserver(self.server)
        cserver = netapp_utils.setup_na_ontap_zapi(module=self.module,
                                                   vserver=results)
        netapp_utils.ems_log_event("na_ontap_cluster_ha", cserver)
        current = self.get_cluster_ha_enabled()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if cd_action == 'create':
            self.modify_cluster_ha("true")
        elif cd_action == 'delete':
            self.modify_cluster_ha("false")

        self.module.exit_json(changed=self.na_helper.changed)
Ejemplo n.º 11
0
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)
Ejemplo n.º 12
0
class NetAppOntapIfGrp(object):
    """
        Create, Modifies and Destroys a IfGrp
    """
    def __init__(self):
        """
            Initialize the Ontap IfGrp class
        """
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(dict(
            state=dict(required=False, choices=['present', 'absent'], default='present'),
            distribution_function=dict(required=False, type='str', choices=['mac', 'ip', 'sequential', 'port']),
            name=dict(required=True, type='str'),
            mode=dict(required=False, type='str'),
            node=dict(required=True, type='str'),
            ports=dict(required=False, type='list', aliases=["port"]),
        ))

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

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

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

    def get_if_grp(self):
        """
        Return details about the if_group
        :param:
            name : Name of the if_group

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

        return_value = None

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

        return return_value

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

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

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

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

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

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

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

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

    def apply(self):
        self.autosupport_log()
        current, modify = self.get_if_grp(), None
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if cd_action is None and self.parameters['state'] == 'present':
            current_ports = self.get_if_grp_ports()
            modify = self.na_helper.get_modified_attributes(current_ports, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_if_grp()
                elif cd_action == 'delete':
                    self.delete_if_grp()
                elif modify:
                    self.modify_ports(current_ports['ports'])
        self.module.exit_json(changed=self.na_helper.changed)
class 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)
class NetAppOntapAggregate(object):
    ''' object initialize and class methods '''
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(name=dict(required=True, type='str'),
                 disks=dict(required=False, type='list'),
                 disk_count=dict(required=False, type='int', default=None),
                 disk_size=dict(required=False, type='int'),
                 disk_type=dict(required=False,
                                choices=[
                                    'ATA', 'BSAS', 'FCAL', 'FSAS', 'LUN',
                                    'MSATA', 'SAS', 'SSD', 'VMDISK'
                                ]),
                 from_name=dict(required=False, type='str'),
                 mirror_disks=dict(required=False, type='list'),
                 nodes=dict(required=False, type='list'),
                 is_mirrored=dict(required=False, type='bool'),
                 raid_size=dict(required=False, type='int'),
                 raid_type=dict(required=False,
                                choices=['raid4', 'raid_dp', 'raid_tec']),
                 service_state=dict(required=False,
                                    choices=['online', 'offline']),
                 spare_pool=dict(required=False, choices=['Pool0', 'Pool1']),
                 state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 unmount_volumes=dict(required=False, type='bool'),
                 wait_for_online=dict(required=False,
                                      type='bool',
                                      default=False),
                 time_out=dict(required=False, type='int', default=100)))

        self.module = AnsibleModule(argument_spec=self.argument_spec,
                                    required_if=[
                                        ('service_state', 'offline',
                                         ['unmount_volumes']),
                                    ],
                                    mutually_exclusive=[
                                        ('is_mirrored', 'disks'),
                                        ('is_mirrored', 'mirror_disks'),
                                        ('is_mirrored', 'spare_pool'),
                                        ('spare_pool', 'disks')
                                    ],
                                    supports_check_mode=True)

        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)
        if self.parameters.get(
                'mirror_disks'
        ) is not None and self.parameters.get('disks') is None:
            self.module.fail_json(
                mgs="mirror_disks require disks options to be set")
        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 aggr_get_iter(self, name):
        """
        Return aggr-get-iter query results
        :param name: Name of the aggregate
        :return: NaElement if aggregate found, None otherwise
        """

        aggr_get_iter = netapp_utils.zapi.NaElement('aggr-get-iter')
        query_details = netapp_utils.zapi.NaElement.create_node_with_children(
            'aggr-attributes', **{'aggregate-name': name})
        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(query_details)
        aggr_get_iter.add_child_elem(query)
        result = None
        try:
            result = self.server.invoke_successfully(aggr_get_iter,
                                                     enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as error:
            # Error 13040 denotes an aggregate not being found.
            if to_native(error.code) == "13040":
                pass
            else:
                self.module.fail_json(msg=to_native(error),
                                      exception=traceback.format_exc())
        return result

    def get_aggr(self, name=None):
        """
        Fetch details if aggregate exists.
        :param name: Name of the aggregate to be fetched
        :return:
            Dictionary of current details if aggregate found
            None if aggregate is not found
        """
        if name is None:
            name = self.parameters['name']
        aggr_get = self.aggr_get_iter(name)
        if (aggr_get and aggr_get.get_child_by_name('num-records')
                and int(aggr_get.get_child_content('num-records')) >= 1):
            current_aggr = dict()
            attr = aggr_get.get_child_by_name(
                'attributes-list').get_child_by_name('aggr-attributes')
            current_aggr['service_state'] = attr.get_child_by_name(
                'aggr-raid-attributes').get_child_content('state')
            return current_aggr
        return None

    def aggregate_online(self):
        """
        Set state of an offline aggregate to online
        :return: None
        """
        online_aggr = netapp_utils.zapi.NaElement.create_node_with_children(
            'aggr-online', **{
                'aggregate': self.parameters['name'],
                'force-online': 'true'
            })
        try:
            self.server.invoke_successfully(online_aggr, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error changing the state of aggregate %s to %s: %s' %
                (self.parameters['name'], self.parameters['service_state'],
                 to_native(error)),
                exception=traceback.format_exc())

    def aggregate_offline(self):
        """
        Set state of an online aggregate to offline
        :return: None
        """
        offline_aggr = netapp_utils.zapi.NaElement.create_node_with_children(
            'aggr-offline', **{
                'aggregate': self.parameters['name'],
                'force-offline': 'false',
                'unmount-volumes': str(self.parameters['unmount_volumes'])
            })
        try:
            self.server.invoke_successfully(offline_aggr,
                                            enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(
                msg='Error changing the state of aggregate %s to %s: %s' %
                (self.parameters['name'], self.parameters['service_state'],
                 to_native(error)),
                exception=traceback.format_exc())

    def create_aggr(self):
        """
        Create aggregate
        :return: None
        """
        if not self.parameters.get('disk_count'):
            self.module.fail_json(msg='Error provisioning aggregate %s: \
                                             disk_count is required' %
                                  self.parameters['name'])
        options = {
            'aggregate': self.parameters['name'],
            'disk-count': str(self.parameters['disk_count'])
        }
        if self.parameters.get('disk_type'):
            options['disk-type'] = self.parameters['disk_type']
        if self.parameters.get('raid_size'):
            options['raid-size'] = str(self.parameters['raid_size'])
        if self.parameters.get('raid_type'):
            options['raid-type'] = self.parameters['raid_type']
        if self.parameters.get('disk_size'):
            options['disk-size'] = str(self.parameters['disk_size'])
        if self.parameters.get('is_mirrored'):
            options['is-mirrored'] = str(self.parameters['is_mirrored'])
        if self.parameters.get('spare_pool'):
            options['spare-pool'] = self.parameters['spare_pool']
        if self.parameters.get('raid_type'):
            options['raid-type'] = self.parameters['raid_type']
        aggr_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'aggr-create', **options)
        if self.parameters.get('nodes'):
            nodes_obj = netapp_utils.zapi.NaElement('nodes')
            aggr_create.add_child_elem(nodes_obj)
            for node in self.parameters['nodes']:
                nodes_obj.add_new_child('node-name', node)
        if self.parameters.get('disks'):
            disks_obj = netapp_utils.zapi.NaElement('disk-info')
            for disk in self.parameters.get('disks'):
                disks_obj.add_new_child('name', disk)
            aggr_create.add_child_elem(disks_obj)
        if self.parameters.get('mirror_disks'):
            mirror_disks_obj = netapp_utils.zapi.NaElement('disk-info')
            for disk in self.parameters.get('mirror_disks'):
                mirror_disks_obj.add_new_child('name', disk)
            aggr_create.add_child_elem(mirror_disks_obj)

        try:
            self.server.invoke_successfully(aggr_create,
                                            enable_tunneling=False)
            if self.parameters.get('wait_for_online'):
                # round off time_out
                retries = (self.parameters['time_out'] + 5) / 10
                current = self.get_aggr()
                status = None if current is None else current['service_state']
                while status != 'online' and retries > 0:
                    time.sleep(10)
                    retries = retries - 1
                    current = self.get_aggr()
                    status = None if current is None else current[
                        'service_state']
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error provisioning aggregate %s: %s" %
                                  (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def delete_aggr(self):
        """
        Delete aggregate.
        :return: None
        """
        aggr_destroy = netapp_utils.zapi.NaElement.create_node_with_children(
            'aggr-destroy', **{'aggregate': self.parameters['name']})

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

    def rename_aggregate(self):
        """
        Rename aggregate.
        """
        aggr_rename = netapp_utils.zapi.NaElement.create_node_with_children(
            'aggr-rename', **{
                'aggregate': self.parameters['from_name'],
                'new-aggregate-name': self.parameters['name']
            })

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

    def modify_aggr(self, modify):
        """
        Modify state of the aggregate
        :param modify: dictionary of parameters to be modified
        :return: None
        """
        if modify['service_state'] == 'offline':
            self.aggregate_offline()
        elif modify['service_state'] == 'online':
            self.aggregate_online()

    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 the aggregate
        :return: None
        """
        self.asup_log_for_cserver("na_ontap_aggregate")

        current = self.get_aggr()
        # rename and create are mutually exclusive
        rename, cd_action = None, None
        if self.parameters.get('from_name'):
            rename = self.na_helper.is_rename_action(
                self.get_aggr(self.parameters['from_name']), current)
            if rename is None:
                self.module.fail_json(
                    msg="Error renaming: aggregate %s does not exist" %
                    self.parameters['from_name'])
        else:
            cd_action = self.na_helper.get_cd_action(current, self.parameters)
        modify = self.na_helper.get_modified_attributes(
            current, self.parameters)

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if rename:
                    self.rename_aggregate()
                elif cd_action == 'create':
                    self.create_aggr()
                elif cd_action == 'delete':
                    self.delete_aggr()
                elif modify:
                    self.modify_aggr(modify)
        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',
                                         ['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)
class NetAppOntapSubnet(object):
    """
    Create, Modifies and Destroys a subnet
    """
    def __init__(self):
        """
        Initialize the ONTAP Subnet class
        """
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(dict(
            state=dict(required=False, choices=['present', 'absent'], default='present'),
            name=dict(required=True, type='str'),
            from_name=dict(required=False, type='str'),
            broadcast_domain=dict(required=False, type='str'),
            gateway=dict(required=False, type='str'),
            ip_ranges=dict(required=False, type=list),
            ipspace=dict(required=False, type='str'),
            subnet=dict(required=False, type='str')
        ))

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

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

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

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

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

        subnet_iter.add_child_elem(query)

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

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

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

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

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

        return return_value

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if rename:
                    self.rename_subnet()
                # If rename is True, cd_action is NOne but modify could be true
                if cd_action == 'create':
                    for attribute in ['subnet', 'broadcast_domain']:
                        if not self.parameters.get(attribute):
                            self.module.fail_json(msg='Error - missing required arguments: %s.' % attribute)
                    self.create_subnet()
                elif cd_action == 'delete':
                    self.delete_subnet()
                elif modify:
                    self.modify_subnet()
        self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapObjectStoreConfig(object):
    ''' object initialize and class methods '''

    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(dict(
            name=dict(required=True, type='str'),
            state=dict(required=False, choices=['present', 'absent'], default='present'),
            provider_type=dict(required=False, type='str'),
            server=dict(required=False, type='str'),
            container=dict(required=False, type='str'),
            access_key=dict(required=False, type='str'),
            secret_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)

    def get_aggr_object_store(self):
        """
        Fetch details if object store config exists.
        :return:
            Dictionary of current details if object store config found
            None if object store config is not found
        """
        aggr_object_store_get_iter = netapp_utils.zapi.NaElement.create_node_with_children(
            'aggr-object-store-config-get', **{'object-store-name': self.parameters['name']})
        result = None
        try:
            result = self.server.invoke_successfully(aggr_object_store_get_iter, enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as error:
            # Error 15661 denotes an object store not being found.
            if to_native(error.code) == "15661":
                pass
            else:
                self.module.fail_json(msg=to_native(error), exception=traceback.format_exc())
        return result

    def create_aggr_object_store(self):
        """
        Create aggregate object store config
        :return: None
        """
        required_keys = set(['provider_type', 'server', 'container', 'access_key'])
        if not required_keys.issubset(set(self.parameters.keys())):
            self.module.fail_json(msg='Error provisioning object store %s: one of the following parameters are missing '
                                      '%s' % (self.parameters['name'], ', '.join(required_keys)))
        options = {'object-store-name': self.parameters['name'],
                   'provider-type': self.parameters['provider_type'],
                   'server': self.parameters['server'],
                   's3-name': self.parameters['container'],
                   'access-key': self.parameters['access_key']}
        if self.parameters.get('secret_password'):
            options['secret-password'] = self.parameters['secret_password']
        object_store_create = netapp_utils.zapi.NaElement.create_node_with_children('aggr-object-store-config-create', **options)

        try:
            self.server.invoke_successfully(object_store_create, enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error provisioning object store config %s: %s"
                                      % (self.parameters['name'], to_native(error)),
                                  exception=traceback.format_exc())

    def delete_aggr_object_store(self):
        """
        Delete aggregate object store config
        :return: None
        """
        object_store_destroy = netapp_utils.zapi.NaElement.create_node_with_children(
            'aggr-object-store-config-delete', **{'object-store-name': self.parameters['name']})

        try:
            self.server.invoke_successfully(object_store_destroy,
                                            enable_tunneling=False)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg="Error removing object store config %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):
        """
        Apply action to the object store config
        :return: None
        """
        self.asup_log_for_cserver("na_ontap_object_store_config")
        current = self.get_aggr_object_store()
        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_aggr_object_store()
                elif cd_action == 'delete':
                    self.delete_aggr_object_store()
        self.module.exit_json(changed=self.na_helper.changed)
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)
Ejemplo n.º 19
0
class NetAppONTAPFlexCache(object):
    """
    Class with FlexCache 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'),
                origin_volume=dict(required=False, type='str'),
                origin_vserver=dict(required=False, type='str'),
                origin_cluster=dict(required=False, type='str'),
                auto_provision_as=dict(required=False, type='str'),
                volume=dict(required=True, type='str'),
                junction_path=dict(required=False, type='str'),
                size=dict(required=False, type='int'),
                size_unit=dict(default='gb',
                               choices=[
                                   'bytes', 'b', 'kb', 'mb', 'gb', 'tb', 'pb',
                                   'eb', 'zb', 'yb'
                               ],
                               type='str'),
                vserver=dict(required=True, type='str'),
                aggr_list=dict(required=False, type='list'),
                aggr_list_multiplier=dict(required=False, type='int'),
                force_offline=dict(required=False, type='bool', default=False),
                force_unmount=dict(required=False, type='bool', default=False),
                time_out=dict(required=False, type='int', default=180),
            ))

        self.module = AnsibleModule(argument_spec=self.argument_spec,
                                    mutually_exclusive=[
                                        ('aggr_list', 'auto_provision_as'),
                                    ],
                                    supports_check_mode=True)

        self.na_helper = NetAppModule()
        self.parameters = self.na_helper.set_parameters(self.module.params)
        if self.parameters.get('size'):
            self.parameters['size'] = self.parameters['size'] * \
                netapp_utils.POW2_BYTE_MAP[self.parameters['size_unit']]
        # setup later if required
        self.origin_server = None
        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 add_parameter_to_dict(self, adict, name, key=None, tostr=False):
        ''' add defined parameter (not None) to adict using key '''
        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 get_job(self, jobid, server):
        """
        Get job details by id
        """
        job_get = netapp_utils.zapi.NaElement('job-get')
        job_get.add_new_child('job-id', jobid)
        try:
            result = server.invoke_successfully(job_get, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            if to_native(error.code) == "15661":
                # Not found
                return None
            self.module.fail_json(msg='Error fetching job info: %s' %
                                  to_native(error),
                                  exception=traceback.format_exc())
        results = dict()
        job_info = result.get_child_by_name('attributes').get_child_by_name(
            'job-info')
        results = {
            'job-progress': job_info['job-progress'],
            'job-state': job_info['job-state']
        }
        if job_info.get_child_by_name('job-completion') is not None:
            results['job-completion'] = job_info['job-completion']
        else:
            results['job-completion'] = None
        return results

    def check_job_status(self, jobid):
        """
        Loop until job is complete
        """
        server = self.server
        sleep_time = 5
        time_out = self.parameters['time_out']
        while time_out > 0:
            results = self.get_job(jobid, server)
            # If running as cluster admin, the job is owned by cluster vserver
            # rather than the target vserver.
            if results is None and server == self.server:
                results = netapp_utils.get_cserver(self.server)
                server = netapp_utils.setup_na_ontap_zapi(module=self.module,
                                                          vserver=results)
                continue
            if results is None:
                error = 'cannot locate job with id: %d' % jobid
                break
            if results['job-state'] in ('queued', 'running'):
                time.sleep(sleep_time)
                time_out -= sleep_time
                continue
            if results['job-state'] in ('success', 'failure'):
                break
            else:
                self.module.fail_json(msg='Unexpected job status in: %s' %
                                      repr(results))

        if results is not None:
            if results['job-state'] == 'success':
                error = None
            elif results['job-state'] in ('queued', 'running'):
                error = 'job completion exceeded expected timer of: %s seconds' % \
                        self.parameters['time_out']
            else:
                if results['job-completion'] is not None:
                    error = results['job-completion']
                else:
                    error = results['job-progress']
        return error

    def flexcache_get_iter(self):
        """
        Compose NaElement object to query current FlexCache relation
        """
        options = {'volume': self.parameters['volume']}
        self.add_parameter_to_dict(options, 'origin_volume', 'origin-volume')
        self.add_parameter_to_dict(options, 'origin_vserver', 'origin-vserver')
        self.add_parameter_to_dict(options, 'origin_cluster', 'origin-cluster')
        flexcache_info = netapp_utils.zapi.NaElement.create_node_with_children(
            'flexcache-info', **options)
        query = netapp_utils.zapi.NaElement('query')
        query.add_child_elem(flexcache_info)
        flexcache_get_iter = netapp_utils.zapi.NaElement('flexcache-get-iter')
        flexcache_get_iter.add_child_elem(query)
        return flexcache_get_iter

    def flexcache_get(self):
        """
        Get current FlexCache relations
        :return: Dictionary of current FlexCache details if query successful, else None
        """
        flexcache_get_iter = self.flexcache_get_iter()
        flex_info = dict()
        try:
            result = self.server.invoke_successfully(flexcache_get_iter,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error fetching FlexCache 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:
            flexcache_info = result.get_child_by_name('attributes-list') \
                                   .get_child_by_name('flexcache-info')
            flex_info['origin_cluster'] = flexcache_info.get_child_content(
                'origin-cluster')
            flex_info['origin_volume'] = flexcache_info.get_child_content(
                'origin-volume')
            flex_info['origin_vserver'] = flexcache_info.get_child_content(
                'origin-vserver')
            flex_info['size'] = flexcache_info.get_child_content('size')
            flex_info['volume'] = flexcache_info.get_child_content('volume')
            flex_info['vserver'] = flexcache_info.get_child_content('vserver')
            flex_info['auto_provision_as'] = flexcache_info.get_child_content(
                'auto-provision-as')

            return flex_info
        if result.get_child_by_name('num-records') and \
                int(result.get_child_content('num-records')) > 1:
            msg = 'Multiple records found for %s:' % self.parameters['volume']
            self.module.fail_json(msg='Error fetching FlexCache info: %s' %
                                  msg)
        return None

    def flexcache_create_async(self):
        """
        Create a FlexCache relationship
        """
        options = {
            'origin-volume': self.parameters['origin_volume'],
            'origin-vserver': self.parameters['origin_vserver'],
            'volume': self.parameters['volume']
        }
        self.add_parameter_to_dict(options, 'junction_path', 'junction-path')
        self.add_parameter_to_dict(options, 'auto_provision_as',
                                   'auto-provision-as')
        self.add_parameter_to_dict(options, 'size', 'size', tostr=True)
        if self.parameters.get('aggr_list'):
            if self.parameters.get('aggr_list_multiplier'):
                self.tobytes_aggr_list_multiplier = bytes(
                    self.parameters['aggr_list_multiplier'])
                self.add_parameter_to_dict(options,
                                           'tobytes_aggr_list_multiplier',
                                           'aggr-list-multiplier')
        flexcache_create = netapp_utils.zapi.NaElement.create_node_with_children(
            'flexcache-create-async', **options)
        if self.parameters.get('aggr_list'):
            aggregates = netapp_utils.zapi.NaElement('aggr-list')
            for aggregate in self.parameters['aggr_list']:
                aggregates.add_new_child('aggr-name', aggregate)
            flexcache_create.add_child_elem(aggregates)
        try:
            result = self.server.invoke_successfully(flexcache_create,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error creating FlexCache %s' %
                                  to_native(error),
                                  exception=traceback.format_exc())
        results = dict()
        for key in ('result-status', 'result-jobid'):
            if result.get_child_by_name(key):
                results[key] = result[key]
        return results

    def flexcache_create(self):
        """
        Create a FlexCache relationship
        Check job status
        """
        results = self.flexcache_create_async()
        status = results.get('result-status')
        if status == 'in_progress' and 'result-jobid' in results:
            if self.parameters['time_out'] == 0:
                # asynchronous call, assuming success!
                return
            error = self.check_job_status(results['result-jobid'])
            if error is None:
                return
            else:
                self.module.fail_json(msg='Error when creating flexcache: %s' %
                                      error)
        self.module.fail_json(
            msg='Unexpected error when creating flexcache: results is: %s' %
            repr(results))

    def flexcache_delete_async(self):
        """
        Delete FlexCache relationship at destination cluster
        """
        options = {'volume': self.parameters['volume']}
        flexcache_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'flexcache-destroy-async', **options)
        try:
            result = self.server.invoke_successfully(flexcache_delete,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error deleting FlexCache : %s' %
                                  (to_native(error)),
                                  exception=traceback.format_exc())
        results = dict()
        for key in ('result-status', 'result-jobid'):
            if result.get_child_by_name(key):
                results[key] = result[key]
        return results

    def volume_offline(self):
        """
        Offline FlexCache volume at destination cluster
        """
        options = {'name': self.parameters['volume']}
        xml = netapp_utils.zapi.NaElement.create_node_with_children(
            'volume-offline', **options)
        try:
            self.server.invoke_successfully(xml, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error offlining FlexCache volume: %s' %
                                  (to_native(error)),
                                  exception=traceback.format_exc())

    def volume_unmount(self):
        """
        Unmount FlexCache volume at destination cluster
        """
        options = {'volume-name': self.parameters['volume']}
        xml = netapp_utils.zapi.NaElement.create_node_with_children(
            'volume-unmount', **options)
        try:
            self.server.invoke_successfully(xml, enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error unmounting FlexCache volume: %s' %
                                  (to_native(error)),
                                  exception=traceback.format_exc())

    def flexcache_delete_async(self):
        """
        Delete FlexCache relationship at destination cluster
        """
        options = {'volume': self.parameters['volume']}
        flexcache_delete = netapp_utils.zapi.NaElement.create_node_with_children(
            'flexcache-destroy-async', **options)
        try:
            result = self.server.invoke_successfully(flexcache_delete,
                                                     enable_tunneling=True)
        except netapp_utils.zapi.NaApiError as error:
            self.module.fail_json(msg='Error deleting FlexCache : %s' %
                                  (to_native(error)),
                                  exception=traceback.format_exc())
        results = dict()
        for key in ('result-status', 'result-jobid'):
            if result.get_child_by_name(key):
                results[key] = result[key]
        return results

    def flexcache_delete(self):
        """
        Delete FlexCache relationship at destination cluster
        Check job status
        """
        if self.parameters['force_unmount']:
            self.volume_unmount()
        if self.parameters['force_offline']:
            self.volume_offline()
        results = self.flexcache_delete_async()
        status = results.get('result-status')
        if status == 'in_progress' and 'result-jobid' in results:
            if self.parameters['time_out'] == 0:
                # asynchronous call, assuming success!
                return
            error = self.check_job_status(results['result-jobid'])
            if error is None:
                return
            else:
                self.module.fail_json(msg='Error when deleting flexcache: %s' %
                                      error)
        self.module.fail_json(
            msg='Unexpected error when deleting flexcache: results is: %s' %
            repr(results))

    def check_parameters(self):
        """
        Validate parameters and fail if one or more required params are missing
        """
        missings = list()
        expected = ('origin_volume', 'origin_vserver')
        if self.parameters['state'] == 'present':
            for param in expected:
                if not self.parameters.get(param):
                    missings.append(param)
        if missings:
            plural = 's' if len(missings) > 1 else ''
            msg = 'Missing parameter%s: %s' % (plural, ', '.join(missings))
            self.module.fail_json(msg=msg)

    def apply(self):
        """
        Apply action to FlexCache
        """
        netapp_utils.ems_log_event("na_ontap_flexcache", self.server)
        current = self.flexcache_get()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if cd_action == 'create':
            self.check_parameters()
            self.flexcache_create()
        elif cd_action == 'delete':
            self.flexcache_delete()
        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)
class NetAppONTAPVserverPeer(object):
    """
    Class with vserver peer methods
    """

    def __init__(self):

        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(dict(
            state=dict(required=False, type='str', choices=['present', 'absent'], default='present'),
            vserver=dict(required=True, type='str'),
            peer_vserver=dict(required=True, type='str'),
            peer_cluster=dict(required=False, type='str'),
            applications=dict(required=False, type='list', choices=['snapmirror', 'file_copy', 'lun_copy', 'flexcache']),
            dest_hostname=dict(required=False, type='str'),
            dest_username=dict(required=False, type='str'),
            dest_password=dict(required=False, type='str', no_log=True)
        ))

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

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

        if HAS_NETAPP_LIB is False:
            self.module.fail_json(msg="the python NetApp-Lib module is required")
        else:
            self.server = netapp_utils.setup_na_ontap_zapi(module=self.module)
            if self.parameters.get('dest_hostname'):
                self.module.params['hostname'] = self.parameters['dest_hostname']
                if self.parameters.get('dest_username'):
                    self.module.params['username'] = self.parameters['dest_username']
                if self.parameters.get('dest_password'):
                    self.module.params['password'] = self.parameters['dest_password']
                self.dest_server = netapp_utils.setup_na_ontap_zapi(module=self.module)
                # reset to source host connection for asup logs
                self.module.params['hostname'] = self.parameters['hostname']

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

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

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

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

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

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

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

    def apply(self):
        """
        Apply action to create/delete or accept vserver peer
        """
        results = netapp_utils.get_cserver(self.server)
        cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results)
        netapp_utils.ems_log_event("na_ontap_vserver_peer", cserver)
        current = self.vserver_peer_get()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if cd_action == 'create':
            self.vserver_peer_create()
            # accept only if the peer relationship is on a remote cluster
            if self.is_remote_peer():
                self.vserver_peer_accept()
        elif cd_action == 'delete':
            self.vserver_peer_delete()

        self.module.exit_json(changed=self.na_helper.changed)
class 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 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)
Ejemplo n.º 24
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 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 NetAppONTAPVolumeClone(object):
    """
        Creates a volume clone
    """

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

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

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

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

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

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

    def apply(self):
        """
        Run Module based on play book
        """
        netapp_utils.ems_log_event("na_ontap_volume_clone", self.server)
        current = self.get_volume_clone()
        cd_action = self.na_helper.get_cd_action(current, self.parameters)
        if self.na_helper.changed:
            if self.module.check_mode:
                pass
            else:
                if cd_action == 'create':
                    self.create_volume_clone()
        self.module.exit_json(changed=self.na_helper.changed)
Ejemplo n.º 27
0
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)
Ejemplo n.º 28
0
class NetAppOntapUnixGroup(object):
    """
    Common operations to manage UNIX groups
    """
    def __init__(self):
        self.argument_spec = netapp_utils.na_ontap_host_argument_spec()
        self.argument_spec.update(
            dict(state=dict(required=False,
                            choices=['present', 'absent'],
                            default='present'),
                 name=dict(required=True, type='str'),
                 id=dict(required=False, type='int'),
                 skip_name_validation=dict(required=False, type='bool'),
                 vserver=dict(required=True, type='str'),
                 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 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)
Ejemplo n.º 30
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)