class NetAppontapExportRule(object): ''' object initialize and class methods ''' def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update(dict( state=dict(required=False, type='str', choices=['present', 'absent'], default='present'), name=dict(required=True, type='str', aliases=['policy_name']), protocol=dict(required=False, type='list', default=None, choices=['any', 'nfs', 'nfs3', 'nfs4', 'cifs', 'flexcache']), client_match=dict(required=False, type='list'), ro_rule=dict(required=False, type='list', default=None, choices=['any', 'none', 'never', 'krb5', 'krb5i', 'krb5p', 'ntlm', 'sys']), rw_rule=dict(required=False, type='list', default=None, choices=['any', 'none', 'never', 'krb5', 'krb5i', 'krb5p', 'ntlm', 'sys']), super_user_security=dict(required=False, type='list', default=None, choices=['any', 'none', 'never', 'krb5', 'krb5i', 'krb5p', 'ntlm', 'sys']), allow_suid=dict(required=False, type='bool'), rule_index=dict(required=False, type='int'), vserver=dict(required=True, type='str'), )) self.module = AnsibleModule( argument_spec=self.argument_spec, supports_check_mode=True ) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) self.set_playbook_zapi_key_map() if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi( module=self.module, vserver=self.parameters['vserver']) def set_playbook_zapi_key_map(self): self.na_helper.zapi_string_keys = { 'client_match': 'client-match', 'name': 'policy-name' } self.na_helper.zapi_list_keys = { 'protocol': ('protocol', 'access-protocol'), 'ro_rule': ('ro-rule', 'security-flavor'), 'rw_rule': ('rw-rule', 'security-flavor'), 'super_user_security': ('super-user-security', 'security-flavor'), } self.na_helper.zapi_bool_keys = { 'allow_suid': 'is-allow-set-uid-enabled' } self.na_helper.zapi_int_keys = { 'rule_index': 'rule-index' } def set_query_parameters(self): """ Return dictionary of query parameters and :return: """ query = { 'policy-name': self.parameters['name'], 'vserver': self.parameters['vserver'] } if self.parameters.get('rule_index'): query['rule-index'] = self.parameters['rule_index'] elif self.parameters.get('client_match'): query['client-match'] = self.parameters['client_match'] else: self.module.fail_json( msg="Need to specify at least one of the rule_index and client_match option.") attributes = { 'query': { 'export-rule-info': query } } return attributes def get_export_policy_rule(self): """ Return details about the export policy rule :param: name : Name of the export_policy :return: Details about the export_policy. None if not found. :rtype: dict """ current, result = None, None rule_iter = netapp_utils.zapi.NaElement('export-rule-get-iter') rule_iter.translate_struct(self.set_query_parameters()) try: result = self.server.invoke_successfully(rule_iter, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error getting export policy rule %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) if result is not None and \ result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1: current = dict() rule_info = result.get_child_by_name('attributes-list').get_child_by_name('export-rule-info') for item_key, zapi_key in self.na_helper.zapi_string_keys.items(): current[item_key] = rule_info.get_child_content(zapi_key) for item_key, zapi_key in self.na_helper.zapi_bool_keys.items(): current[item_key] = self.na_helper.get_value_for_bool(from_zapi=True, value=rule_info[zapi_key]) for item_key, zapi_key in self.na_helper.zapi_int_keys.items(): current[item_key] = self.na_helper.get_value_for_int(from_zapi=True, value=rule_info[zapi_key]) for item_key, zapi_key in self.na_helper.zapi_list_keys.items(): parent, dummy = zapi_key current[item_key] = self.na_helper.get_value_for_list(from_zapi=True, zapi_parent=rule_info.get_child_by_name(parent)) current['num_records'] = int(result.get_child_content('num-records')) if not self.parameters.get('rule_index'): self.parameters['rule_index'] = current['rule_index'] return current def get_export_policy(self): """ Return details about the export-policy :param: name : Name of the export-policy :return: Details about the export-policy. None if not found. :rtype: dict """ export_policy_iter = netapp_utils.zapi.NaElement('export-policy-get-iter') attributes = { 'query': { 'export-policy-info': { 'policy-name': self.parameters['name'], 'vserver': self.parameters['vserver'] } } } export_policy_iter.translate_struct(attributes) try: result = self.server.invoke_successfully(export_policy_iter, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error getting export policy %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) == 1: return result return None def add_parameters_for_create_or_modify(self, na_element_object, values): """ Add children node for create or modify NaElement object :param na_element_object: modify or create NaElement object :param values: dictionary of cron values to be added :return: None """ for key in values: if key in self.na_helper.zapi_string_keys: zapi_key = self.na_helper.zapi_string_keys.get(key) na_element_object[zapi_key] = values[key] elif key in self.na_helper.zapi_list_keys: parent_key, child_key = self.na_helper.zapi_list_keys.get(key) na_element_object.add_child_elem(self.na_helper.get_value_for_list(from_zapi=False, zapi_parent=parent_key, zapi_child=child_key, data=values[key])) elif key in self.na_helper.zapi_int_keys: zapi_key = self.na_helper.zapi_int_keys.get(key) na_element_object[zapi_key] = self.na_helper.get_value_for_int(from_zapi=False, value=values[key]) elif key in self.na_helper.zapi_bool_keys: zapi_key = self.na_helper.zapi_bool_keys.get(key) na_element_object[zapi_key] = self.na_helper.get_value_for_bool(from_zapi=False, value=values[key]) def create_export_policy_rule(self): """ create rule for the export policy. """ for key in ['client_match', 'ro_rule', 'rw_rule']: if self.parameters.get(key) is None: self.module.fail_json(msg='Error: Missing required param for creating export policy rule %s' % key) export_rule_create = netapp_utils.zapi.NaElement('export-rule-create') self.add_parameters_for_create_or_modify(export_rule_create, self.parameters) try: self.server.invoke_successfully(export_rule_create, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error creating export policy rule %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def create_export_policy(self): """ Creates an export policy """ export_policy_create = netapp_utils.zapi.NaElement.create_node_with_children( 'export-policy-create', **{'policy-name': self.parameters['name']}) try: self.server.invoke_successfully(export_policy_create, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error creating export-policy %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def delete_export_policy_rule(self, rule_index): """ delete rule for the export policy. """ export_rule_delete = netapp_utils.zapi.NaElement.create_node_with_children( 'export-rule-destroy', **{'policy-name': self.parameters['name'], 'rule-index': str(rule_index)}) try: self.server.invoke_successfully(export_rule_delete, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error deleting export policy rule %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def modify_export_policy_rule(self, params): ''' Modify an existing export policy rule :param params: dict() of attributes with desired values :return: None ''' export_rule_modify = netapp_utils.zapi.NaElement.create_node_with_children( 'export-rule-modify', **{'policy-name': self.parameters['name'], 'rule-index': str(self.parameters['rule_index'])}) self.add_parameters_for_create_or_modify(export_rule_modify, params) try: self.server.invoke_successfully(export_rule_modify, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error modifying allow_suid %s: %s' % (self.parameters['allow_suid'], to_native(error)), exception=traceback.format_exc()) def autosupport_log(self): netapp_utils.ems_log_event("na_ontap_export_policy_rules", self.server) def apply(self): ''' Apply required action from the play''' self.autosupport_log() # convert client_match list to comma-separated string if self.parameters.get('client_match') is not None: self.parameters['client_match'] = ','.join(self.parameters['client_match']) self.parameters['client_match'] = self.parameters['client_match'].replace(' ', '') current, modify = self.get_export_policy_rule(), None action = self.na_helper.get_cd_action(current, self.parameters) if action is None and self.parameters['state'] == 'present': modify = self.na_helper.get_modified_attributes(current, self.parameters) if self.na_helper.changed: if self.module.check_mode: pass else: # create export policy (if policy doesn't exist) only when changed=True if not self.get_export_policy(): self.create_export_policy() if action == 'create': self.create_export_policy_rule() elif action == 'delete': if current['num_records'] > 1: self.module.fail_json(msg='Multiple export policy rules exist.' 'Please specify a rule_index to delete') self.delete_export_policy_rule(current['rule_index']) elif modify: self.modify_export_policy_rule(modify) self.module.exit_json(changed=self.na_helper.changed)
class 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 NetAppOntapServiceProcessorNetwork(object): """ Modify a Service Processor Network """ def __init__(self): """ Initialize the NetAppOntapServiceProcessorNetwork class """ self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update( dict(state=dict(required=False, choices=['present'], default='present'), address_type=dict(required=True, choices=['ipv4', 'ipv6']), is_enabled=dict(required=True, type='bool'), node=dict(required=True, type='str'), dhcp=dict(required=False, choices=['v4', 'none']), gateway_ip_address=dict(required=False, type='str'), ip_address=dict(required=False, type='str'), netmask=dict(required=False, type='str'), prefix_length=dict(required=False, type='int'), wait_for_completion=dict(required=False, type='bool', default=False))) self.module = AnsibleModule(argument_spec=self.argument_spec, supports_check_mode=True) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) self.set_playbook_zapi_key_map() if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=None) return def set_playbook_zapi_key_map(self): self.na_helper.zapi_string_keys = { 'address_type': 'address-type', 'node': 'node', 'dhcp': 'dhcp', 'gateway_ip_address': 'gateway-ip-address', 'ip_address': 'ip-address', 'netmask': 'netmask' } self.na_helper.zapi_int_keys = {'prefix_length': 'prefix-length'} self.na_helper.zapi_bool_keys = { 'is_enabled': 'is-enabled', } self.na_helper.zapi_required = { 'address_type': 'address-type', 'node': 'node', 'is_enabled': 'is-enabled' } def get_sp_network_status(self): """ Return status of service processor network :param: name : name of the node :return: Status of the service processor network :rtype: dict """ spn_get_iter = netapp_utils.zapi.NaElement( 'service-processor-network-get-iter') query_info = { 'query': { 'service-processor-network-info': { 'node': self.parameters['node'], 'address-type': self.parameters['address_type'] } } } spn_get_iter.translate_struct(query_info) result = self.server.invoke_successfully(spn_get_iter, True) if int(result['num-records']) >= 1: sp_attr_info = result['attributes-list'][ 'service-processor-network-info'] return sp_attr_info.get_child_content('setup-status') return None def get_service_processor_network(self): """ Return details about service processor network :param: name : name of the node :return: Details about service processor network. None if not found. :rtype: dict """ spn_get_iter = netapp_utils.zapi.NaElement( 'service-processor-network-get-iter') query_info = { 'query': { 'service-processor-network-info': { 'node': self.parameters['node'] } } } spn_get_iter.translate_struct(query_info) result = self.server.invoke_successfully(spn_get_iter, True) sp_details = None # check if job exists if int(result['num-records']) >= 1: sp_details = dict() sp_attr_info = result['attributes-list'][ 'service-processor-network-info'] for item_key, zapi_key in self.na_helper.zapi_string_keys.items(): sp_details[item_key] = sp_attr_info.get_child_content(zapi_key) for item_key, zapi_key in self.na_helper.zapi_bool_keys.items(): sp_details[item_key] = self.na_helper.get_value_for_bool( from_zapi=True, value=sp_attr_info.get_child_content(zapi_key)) for item_key, zapi_key in self.na_helper.zapi_int_keys.items(): sp_details[item_key] = self.na_helper.get_value_for_int( from_zapi=True, value=sp_attr_info.get_child_content(zapi_key)) return sp_details def modify_service_processor_network(self, params=None): """ Modify a service processor network. :param params: A dict of modified options. When dhcp is not set to v4, ip_address, netmask, and gateway_ip_address must be specified even if remains the same. """ if self.parameters['is_enabled'] is False: if params.get('is_enabled') and len(params) > 1: self.module.fail_json( msg= 'Error: Cannot modify any other parameter for a service processor network if option "is_enabled" is set to false.' ) elif params.get('is_enabled') is None and len(params) > 0: self.module.fail_json( msg= 'Error: Cannot modify a service processor network if it is disabled.' ) sp_modify = netapp_utils.zapi.NaElement( 'service-processor-network-modify') sp_modify.add_new_child("node", self.parameters['node']) sp_modify.add_new_child("address-type", self.parameters['address_type']) sp_attributes = dict() for item_key in self.parameters: if item_key in self.na_helper.zapi_string_keys: zapi_key = self.na_helper.zapi_string_keys.get(item_key) sp_attributes[zapi_key] = self.parameters[item_key] elif item_key in self.na_helper.zapi_bool_keys: zapi_key = self.na_helper.zapi_bool_keys.get(item_key) sp_attributes[zapi_key] = self.na_helper.get_value_for_bool( from_zapi=False, value=self.parameters[item_key]) elif item_key in self.na_helper.zapi_int_keys: zapi_key = self.na_helper.zapi_int_keys.get(item_key) sp_attributes[zapi_key] = self.na_helper.get_value_for_int( from_zapi=False, value=self.parameters[item_key]) sp_modify.translate_struct(sp_attributes) try: self.server.invoke_successfully(sp_modify, enable_tunneling=True) if self.parameters.get('wait_for_completion'): retries = 10 while self.get_sp_network_status( ) == 'in_progress' and retries > 0: time.sleep(10) retries = retries - 1 except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg='Error modifying service processor network: %s' % (to_native(error)), exception=traceback.format_exc()) def autosupport_log(self): results = netapp_utils.get_cserver(self.server) cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results) netapp_utils.ems_log_event("na_ontap_service_processor_network", cserver) def apply(self): """ Run Module based on play book """ self.autosupport_log() current = self.get_service_processor_network() modify = self.na_helper.get_modified_attributes( current, self.parameters) if not current: self.module.fail_json( msg='Error No Service Processor for node: %s' % self.parameters['node']) if self.na_helper.changed: if self.module.check_mode: pass else: self.modify_service_processor_network(modify) self.module.exit_json(changed=self.na_helper.changed)