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, type='str', choices=['present'], default='present'), address_type=dict(required=True, type='str', choices=['ipv4', 'ipv6']), is_enabled=dict(required=True, type='bool'), node=dict(required=True, type='str'), dhcp=dict(required=False, type='str', 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)
class NetAppONTAPCifsSecurity(object): ''' modify vserver cifs security ''' def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update(dict( vserver=dict(required=True, type='str'), kerberos_clock_skew=dict(required=False, type='int'), kerberos_ticket_age=dict(required=False, type='int'), kerberos_renew_age=dict(required=False, type='int'), kerberos_kdc_timeout=dict(required=False, type='int'), is_signing_required=dict(required=False, type='bool'), is_password_complexity_required=dict(required=False, type='bool'), is_aes_encryption_enabled=dict(required=False, type='bool'), is_smb_encryption_required=dict(required=False, type='bool'), lm_compatibility_level=dict(required=False, choices=['lm_ntlm_ntlmv2_krb', 'ntlm_ntlmv2_krb', 'ntlmv2_krb', 'krb']), referral_enabled_for_ad_ldap=dict(required=False, type='bool'), session_security_for_ad_ldap=dict(required=False, choices=['none', 'sign', 'seal']), smb1_enabled_for_dc_connections=dict(required=False, choices=['false', 'true', 'system_default']), smb2_enabled_for_dc_connections=dict(required=False, choices=['false', 'true', 'system_default']), use_start_tls_for_ad_ldap=dict(required=False, type='bool') )) self.module = AnsibleModule( argument_spec=self.argument_spec, supports_check_mode=True ) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) 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_int_keys = { 'kerberos_clock_skew': 'kerberos-clock-skew', 'kerberos_ticket_age': 'kerberos-ticket-age', 'kerberos_renew_age': 'kerberos-renew-age', 'kerberos_kdc_timeout': 'kerberos-kdc-timeout' } self.na_helper.zapi_bool_keys = { 'is_signing_required': 'is-signing-required', 'is_password_complexity_required': 'is-password-complexity-required', 'is_aes_encryption_enabled': 'is-aes-encryption-enabled', 'is_smb_encryption_required': 'is-smb-encryption-required', 'referral_enabled_for_ad_ldap': 'referral-enabled-for-ad-ldap', 'use_start_tls_for_ad_ldap': 'use-start-tls-for-ad-ldap' } self.na_helper.zapi_str_keys = { 'lm_compatibility_level': 'lm-compatibility-level', 'session_security_for_ad_ldap': 'session-security-for-ad-ldap', 'smb1_enabled_for_dc_connections': 'smb1-enabled-for-dc-connections', 'smb2_enabled_for_dc_connections': 'smb2-enabled-for-dc-connections' } def cifs_security_get_iter(self): """ get current vserver cifs security. :return: a dict of vserver cifs security """ cifs_security_get = netapp_utils.zapi.NaElement('cifs-security-get-iter') query = netapp_utils.zapi.NaElement('query') cifs_security = netapp_utils.zapi.NaElement('cifs-security') cifs_security.add_new_child('vserver', self.parameters['vserver']) query.add_child_elem(cifs_security) cifs_security_get.add_child_elem(query) cifs_security_details = dict() try: result = self.server.invoke_successfully(cifs_security_get, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error fetching cifs security from %s: %s' % (self.parameters['vserver'], to_native(error)), exception=traceback.format_exc()) if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) > 0: cifs_security_info = result.get_child_by_name('attributes-list').get_child_by_name('cifs-security') for option, zapi_key in self.na_helper.zapi_int_keys.items(): cifs_security_details[option] = self.na_helper.get_value_for_int(from_zapi=True, value=cifs_security_info.get_child_content(zapi_key)) for option, zapi_key in self.na_helper.zapi_bool_keys.items(): cifs_security_details[option] = self.na_helper.get_value_for_bool(from_zapi=True, value=cifs_security_info.get_child_content(zapi_key)) for option, zapi_key in self.na_helper.zapi_str_keys.items(): if cifs_security_info.get_child_content(zapi_key) is None: cifs_security_details[option] = None else: cifs_security_details[option] = cifs_security_info.get_child_content(zapi_key) return cifs_security_details return None def cifs_security_modify(self, modify): """ :param modify: A list of attributes to modify :return: None """ cifs_security_modify = netapp_utils.zapi.NaElement('cifs-security-modify') for attribute in modify: cifs_security_modify.add_new_child(self.attribute_to_name(attribute), str(self.parameters[attribute])) try: self.server.invoke_successfully(cifs_security_modify, enable_tunneling=True) except netapp_utils.zapi.NaApiError as e: self.module.fail_json(msg='Error modifying cifs security on %s: %s' % (self.parameters['vserver'], to_native(e)), exception=traceback.format_exc()) @staticmethod def attribute_to_name(attribute): return str.replace(attribute, '_', '-') def apply(self): """Call modify operations.""" self.asup_log_for_cserver("na_ontap_vserver_cifs_security") current = self.cifs_security_get_iter() modify = self.na_helper.get_modified_attributes(current, self.parameters) if self.na_helper.changed: if self.module.check_mode: pass else: if modify: self.cifs_security_modify(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 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', elements='str', default=None, choices=[ 'any', 'nfs', 'nfs3', 'nfs4', 'cifs', 'flexcache' ]), client_match=dict(required=False, type='list', elements='str'), ro_rule=dict(required=False, type='list', elements='str', default=None, choices=[ 'any', 'none', 'never', 'krb5', 'krb5i', 'krb5p', 'ntlm', 'sys' ]), rw_rule=dict(required=False, type='list', elements='str', default=None, choices=[ 'any', 'none', 'never', 'krb5', 'krb5i', 'krb5p', 'ntlm', 'sys' ]), super_user_security=dict(required=False, type='list', elements='str', 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'), anonymous_user_id=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', 'anonymous_user_id': 'anonymous-user-id' } 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 action == 'create': if not self.get_export_policy(): self.create_export_policy() 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 NetAppOntapLogForward(object): def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update( dict(state=dict(choices=['present', 'absent'], default='present'), destination=dict(required=True, type='str'), port=dict(required=True, type='int'), facility=dict(required=False, type='str', choices=[ 'kern', 'user', 'local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7' ]), force=dict(required=False, type='bool'), protocol=dict(required=False, type='str', choices=[ 'udp_unencrypted', 'tcp_unencrypted', 'tcp_encrypted' ]), verify_server=dict(required=False, type='bool'))) self.module = AnsibleModule(argument_spec=self.argument_spec, supports_check_mode=True) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) self.rest_api = OntapRestAPI(self.module) self.use_rest = self.rest_api.is_rest() if not self.use_rest: if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi( module=self.module) def get_log_forward_config(self): """ gets log forward configuration :return: dict of log forward properties if exist, None if not """ if self.use_rest: log_forward_config = None api = "security/audit/destinations" query = { 'fields': 'port,protocol,facility,address,verify_server', 'address': self.parameters['destination'], 'port': self.parameters['port'] } message, error = self.rest_api.get(api, query) if error: self.module.fail_json(msg=error) if len(message.keys()) == 0: return None elif 'records' in message and len(message['records']) == 0: return None elif 'records' not in message: error = "Unexpected response in get_security_key_manager from %s: %s" % ( api, repr(message)) self.module.fail_json(msg=error) log_forward_config = { 'destination': message['records'][0]['address'], 'facility': message['records'][0]['facility'], 'port': message['records'][0]['port'], 'protocol': message['records'][0]['protocol'], 'verify_server': message['records'][0]['verify_server'] } return log_forward_config else: log_forward_config = None log_forward_get = netapp_utils.zapi.NaElement( 'cluster-log-forward-get') log_forward_get.add_new_child('destination', self.parameters['destination']) log_forward_get.add_new_child( 'port', self.na_helper.get_value_for_int(False, self.parameters['port'])) try: result = self.server.invoke_successfully(log_forward_get, True) except netapp_utils.zapi.NaApiError as error: if to_native(error.code) == "15661": # config doesnt exist return None else: self.module.fail_json( msg= 'Error getting log forward configuration for destination %s on port %s: %s' % (self.parameters['destination'], self.na_helper.get_value_for_int( False, self.parameters['port']), to_native(error)), exception=traceback.format_exc()) if result.get_child_by_name('attributes'): log_forward_attributes = result.get_child_by_name('attributes') cluster_log_forward_info = log_forward_attributes.get_child_by_name( 'cluster-log-forward-info') log_forward_config = { 'destination': cluster_log_forward_info.get_child_content('destination'), 'facility': cluster_log_forward_info.get_child_content('facility'), 'port': self.na_helper.get_value_for_int( True, cluster_log_forward_info.get_child_content('port')), 'protocol': cluster_log_forward_info.get_child_content('protocol'), 'verify_server': self.na_helper.get_value_for_bool( True, cluster_log_forward_info.get_child_content( 'verify-server')) } return log_forward_config def create_log_forward_config(self): """ Creates a log forward config :return: nothing """ if self.use_rest: api = "security/audit/destinations" body = dict() body['address'] = self.parameters['destination'] body['port'] = self.parameters['port'] for attr in ('protocol', 'facility', 'verify_server', 'force'): if attr in self.parameters: body[attr] = self.parameters[attr] dummy, error = self.rest_api.post(api, body) if error: self.module.fail_json(msg=error) else: log_forward_config_obj = netapp_utils.zapi.NaElement( 'cluster-log-forward-create') log_forward_config_obj.add_new_child( 'destination', self.parameters['destination']) log_forward_config_obj.add_new_child( 'port', self.na_helper.get_value_for_int(False, self.parameters['port'])) if 'facility' in self.parameters: log_forward_config_obj.add_new_child( 'facility', self.parameters['facility']) if 'force' in self.parameters: log_forward_config_obj.add_new_child( 'force', self.na_helper.get_value_for_bool( False, self.parameters['force'])) if 'protocol' in self.parameters: log_forward_config_obj.add_new_child( 'protocol', self.parameters['protocol']) if 'verify_server' in self.parameters: log_forward_config_obj.add_new_child( 'verify-server', self.na_helper.get_value_for_bool( False, self.parameters['verify_server'])) try: self.server.invoke_successfully(log_forward_config_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg= 'Error creating log forward config with destination %s on port %s: %s' % (self.parameters['destination'], self.na_helper.get_value_for_int( False, self.parameters['port']), to_native(error)), exception=traceback.format_exc()) def modify_log_forward_config(self): # need to recreate as protocol can't be changed self.destroy_log_forward_config() self.create_log_forward_config() def destroy_log_forward_config(self): """ Delete a log forward configuration :return: nothing """ if self.use_rest: api = "security/audit/destinations/%s/%s" % ( self.parameters['destination'], self.parameters['port']) body = None query = {'return_timeout': 3} dummy, error = self.rest_api.delete(api, body, query) if error: self.module.fail_json(msg=error) else: log_forward_config_obj = netapp_utils.zapi.NaElement( 'cluster-log-forward-destroy') log_forward_config_obj.add_new_child( 'destination', self.parameters['destination']) log_forward_config_obj.add_new_child( 'port', self.na_helper.get_value_for_int(False, self.parameters['port'])) try: self.server.invoke_successfully(log_forward_config_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg= 'Error destroying log forward destination %s on port %s: %s' % (self.parameters['destination'], self.na_helper.get_value_for_int( False, self.parameters['port']), to_native(error)), exception=traceback.format_exc()) def ems_log_event(self): results = netapp_utils.get_cserver(self.server) cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results) return netapp_utils.ems_log_event("na_ontap_log_forward", cserver) def apply(self): if not self.use_rest: self.ems_log_event() current = self.get_log_forward_config() 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 not self.module.check_mode: if cd_action == 'create': self.create_log_forward_config() elif cd_action == 'delete': self.destroy_log_forward_config() elif modify: self.modify_log_forward_config() self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapEfficiencyPolicy(object): """ Create, delete and modify efficiency 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'), policy_name=dict(required=True, type='str'), comment=dict(required=False, type='str'), duration=dict(required=False, type='int'), enabled=dict(required=False, type='bool'), policy_type=dict(required=False, choices=['threshold', 'scheduled']), qos_policy=dict(required=False, choices=['background', 'best_effort']), schedule=dict(reuired=False, type='str'), vserver=dict(required=True, type='str'), changelog_threshold_percent=dict(required=False, type='int'))) self.module = AnsibleModule( argument_spec=self.argument_spec, supports_check_mode=True, mutually_exclusive=[('changelog_threshold_percent', 'duration'), ('changelog_threshold_percent', 'schedule')]) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) self.set_playbook_zapi_key_map() if self.parameters.get('policy_type'): if self.parameters['policy_type'] == 'threshold': if self.parameters.get('duration'): self.module.fail_json( msg="duration cannot be set if policy_type is threshold" ) if self.parameters.get('schedule'): self.module.fail_json( msg='schedule cannot be set if policy_type is threshold' ) # if policy_type is 'scheduled' else: if self.parameters.get('changelog_threshold_percent'): self.module.fail_json( msg= 'changelog_threshold_percent cannot be set if policy_type is scheduled' ) 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_int_keys = { 'changelog_threshold_percent': 'changelog-threshold-percent', 'duration': 'duration', } self.na_helper.zapi_str_keys = { 'policy_name': 'policy-name', 'comment': 'comment', 'policy_type': 'policy-type', 'qos_policy': 'qos-policy', 'schedule': 'schedule' } self.na_helper.zapi_bool_keys = {'enabled': 'enabled'} def get_efficiency_policy(self): """ Get a efficiency policy :return: a efficiency-policy info """ sis_policy_obj = netapp_utils.zapi.NaElement("sis-policy-get-iter") query = netapp_utils.zapi.NaElement("query") sis_policy_info = netapp_utils.zapi.NaElement("sis-policy-info") sis_policy_info.add_new_child("policy-name", self.parameters['policy_name']) sis_policy_info.add_new_child("vserver", self.parameters['vserver']) query.add_child_elem(sis_policy_info) sis_policy_obj.add_child_elem(query) try: results = self.server.invoke_successfully(sis_policy_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg="Error searching for efficiency policy %s: %s" % (self.parameters['policy_name'], to_native(error)), exception=traceback.format_exc()) return_value = {} if results.get_child_by_name('num-records') and int( results.get_child_content('num-records')) == 1: attributes_list = results.get_child_by_name('attributes-list') sis_info = attributes_list.get_child_by_name('sis-policy-info') for option, zapi_key in self.na_helper.zapi_int_keys.items(): return_value[option] = self.na_helper.get_value_for_int( from_zapi=True, value=sis_info.get_child_content(zapi_key)) for option, zapi_key in self.na_helper.zapi_bool_keys.items(): return_value[option] = self.na_helper.get_value_for_bool( from_zapi=True, value=sis_info.get_child_content(zapi_key)) for option, zapi_key in self.na_helper.zapi_str_keys.items(): return_value[option] = sis_info.get_child_content(zapi_key) return return_value return None def create_efficiency_policy(self): """ Creates a efficiency policy :return: None """ sis_policy_obj = netapp_utils.zapi.NaElement("sis-policy-create") for option, zapi_key in self.na_helper.zapi_int_keys.items(): if self.parameters.get(option): sis_policy_obj.add_new_child( zapi_key, self.na_helper.get_value_for_int( from_zapi=False, value=self.parameters[option])) for option, zapi_key in self.na_helper.zapi_bool_keys.items(): if self.parameters.get(option): sis_policy_obj.add_new_child( zapi_key, self.na_helper.get_value_for_bool( from_zapi=False, value=self.parameters[option])) for option, zapi_key in self.na_helper.zapi_str_keys.items(): if self.parameters.get(option): sis_policy_obj.add_new_child(zapi_key, str(self.parameters[option])) try: self.server.invoke_successfully(sis_policy_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg="Error creating efficiency policy %s: %s" % (self.parameters["policy_name"], to_native(error)), exception=traceback.format_exc()) def delete_efficiency_policy(self): """ Delete a efficiency Policy :return: None """ sis_policy_obj = netapp_utils.zapi.NaElement("sis-policy-delete") sis_policy_obj.add_new_child("policy-name", self.parameters['policy_name']) try: self.server.invoke_successfully(sis_policy_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg="Error deleting efficiency policy %s: %s" % (self.parameters["policy_name"], to_native(error)), exception=traceback.format_exc()) def modify_efficiency_policy(self, current, modify): """ Modify a efficiency policy :return: None """ sis_policy_obj = netapp_utils.zapi.NaElement("sis-policy-modify") sis_policy_obj.add_new_child("policy-name", self.parameters['policy_name']) # sis-policy-create zapi pre-checks the options and fails if it's not supported. # sis-policy-modify pre-checks one of the options, but tries to modify the others even it's not supported. And it will mess up the vsim. # Do the checks before sending to the zapi. if current['policy_type'] == 'scheduled' and self.parameters.get( 'policy_type') != 'threshold': if modify.get('changelog_threshold_percent'): self.module.fail_json( msg= "changelog_threshold_percent cannot be set if policy_type is scheduled" ) elif current['policy_type'] == 'threshold' and self.parameters.get( 'policy_type') != 'scheduled': if modify.get('duration'): self.module.fail_json( msg="duration cannot be set if policy_type is threshold") elif modify.get('schedule'): self.module.fail_json( msg="schedule cannot be set if policy_type is threshold") for attribute in modify: sis_policy_obj.add_new_child(self.attribute_to_name(attribute), str(self.parameters[attribute])) try: self.server.invoke_successfully(sis_policy_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg="Error modifying efficiency policy %s: %s" % (self.parameters["policy_name"], to_native(error)), exception=traceback.format_exc()) @staticmethod def attribute_to_name(attribute): return str.replace(attribute, '_', '-') def apply(self): netapp_utils.ems_log_event("na_ontap_efficiency_policy", self.server) current = self.get_efficiency_policy() cd_action = self.na_helper.get_cd_action(current, self.parameters) if cd_action is None and self.parameters['state'] == 'present': modify = self.na_helper.get_modified_attributes( current, self.parameters) if self.na_helper.changed: if self.module.check_mode: pass else: if cd_action == 'create': self.create_efficiency_policy() elif cd_action == 'delete': self.delete_efficiency_policy() elif modify: self.modify_efficiency_policy(current, modify) self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapLDAPClient(object): ''' LDAP Client definition class ''' def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update( dict(ad_domain=dict(required=False, default=None, type='str'), base_dn=dict(required=False, type='str'), base_scope=dict(required=False, default=None, choices=['subtree', 'onelevel', 'base']), bind_as_cifs_server=dict(required=False, type='bool'), bind_dn=dict(required=False, default=None, type='str'), bind_password=dict(type='str', required=False, default=None, no_log=True), name=dict(required=True, type='str'), ldap_servers=dict(required=False, type='list', elements='str'), min_bind_level=dict(required=False, default=None, choices=['anonymous', 'simple', 'sasl']), preferred_ad_servers=dict(required=False, type='list', elements='str'), port=dict(required=False, default=None, type='int'), query_timeout=dict(required=False, default=None, type='int'), referral_enabled=dict(required=False, type='bool'), schema=dict( required=False, default=None, choices=['AD-IDMU', 'AD-SFU', 'MS-AD-BIS', 'RFC-2307']), session_security=dict(required=False, default=None, choices=['none', 'sign', 'seal']), state=dict(required=False, choices=['present', 'absent'], default='present'), use_start_tls=dict(required=False, type='bool'), vserver=dict(required=True, type='str'))) self.module = AnsibleModule( argument_spec=self.argument_spec, supports_check_mode=True, required_if=[ ('state', 'present', ['schema']), ], mutually_exclusive=[['ldap_servers', 'ad_domain'], ['ldap_servers', 'preferred_ad_servers']], ) 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 = [ 'ad_domain', 'base_dn', 'base_scope', 'bind_as_cifs_server', 'bind_dn', 'bind_password', 'min_bind_level', 'port', 'query_timeout', 'referral_enabled', 'session_security', 'use_start_tls' ] def get_ldap_client(self, client_config_name=None, vserver_name=None): ''' Checks if LDAP client config exists. :return: ldap client config object if found None if not found :rtype: object/None ''' # Make query client_config_info = netapp_utils.zapi.NaElement( 'ldap-client-get-iter') if client_config_name is None: client_config_name = self.parameters['name'] if vserver_name is None: vserver_name = '*' query_details = netapp_utils.zapi.NaElement.create_node_with_children( 'ldap-client', **{ 'ldap-client-config': client_config_name, 'vserver': vserver_name }) query = netapp_utils.zapi.NaElement('query') query.add_child_elem(query_details) client_config_info.add_child_elem(query) result = self.server.invoke_successfully(client_config_info, enable_tunneling=False) # Get LDAP client configuration details client_config_details = None if result.get_child_by_name('num-records') and int( result.get_child_content('num-records')) >= 1: attributes_list = result.get_child_by_name('attributes-list') client_config_info = attributes_list.get_child_by_name( 'ldap-client') # Get LDAP servers list ldap_server_list = list() get_list = client_config_info.get_child_by_name('ldap-servers') if get_list is not None: ldap_server_list = [ x.get_content() for x in get_list.get_children() ] preferred_ad_servers_list = list() get_pref_ad_server_list = client_config_info.get_child_by_name( 'preferred-ad-servers') if get_pref_ad_server_list is not None: preferred_ad_servers_list = [ x.get_content() for x in get_pref_ad_server_list.get_children() ] # Define config details structure client_config_details = { 'name': client_config_info.get_child_content('ldap-client-config'), 'ldap_servers': ldap_server_list, 'ad_domain': client_config_info.get_child_content('ad-domain'), 'base_dn': client_config_info.get_child_content('base-dn'), 'base_scope': client_config_info.get_child_content('base-scope'), 'bind_as_cifs_server': self.na_helper.get_value_for_bool( from_zapi=True, value=client_config_info.get_child_content( 'bind-as-cifs-server')), 'bind_dn': client_config_info.get_child_content('bind-dn'), 'bind_password': client_config_info.get_child_content('bind-password'), 'min_bind_level': client_config_info.get_child_content('min-bind-level'), 'port': self.na_helper.get_value_for_int( from_zapi=True, value=client_config_info.get_child_content('port')), 'preferred_ad_servers': preferred_ad_servers_list, 'query_timeout': self.na_helper.get_value_for_int( from_zapi=True, value=client_config_info.get_child_content( 'query-timeout')), 'referral_enabled': self.na_helper.get_value_for_bool( from_zapi=True, value=client_config_info.get_child_content( 'referral-enabled')), 'schema': client_config_info.get_child_content('schema'), 'session_security': client_config_info.get_child_content('session-security'), 'use_start_tls': self.na_helper.get_value_for_bool( from_zapi=True, value=client_config_info.get_child_content( 'use-start-tls')) } return client_config_details def create_ldap_client(self): ''' Create LDAP client configuration ''' options = { 'ldap-client-config': self.parameters['name'], 'schema': self.parameters['schema'], } # Other options/attributes for attribute in self.simple_attributes: if self.parameters.get(attribute) is not None: options[str(attribute).replace('_', '-')] = str( self.parameters[attribute]) # Initialize NaElement ldap_client_create = netapp_utils.zapi.NaElement.create_node_with_children( 'ldap-client-create', **options) # LDAP servers NaElement if self.parameters.get('ldap_servers') is not None: ldap_servers_element = netapp_utils.zapi.NaElement('ldap-servers') for ldap_server_name in self.parameters['ldap_servers']: ldap_servers_element.add_new_child('string', ldap_server_name) ldap_client_create.add_child_elem(ldap_servers_element) # preferred_ad_servers if self.parameters.get('preferred_ad_servers') is not None: preferred_ad_servers_element = netapp_utils.zapi.NaElement( 'preferred-ad-servers') for pref_ad_server in self.parameters['preferred_ad_servers']: preferred_ad_servers_element.add_new_child( 'ip-address', pref_ad_server) ldap_client_create.add_child_elem(preferred_ad_servers_element) # Try to create LDAP configuration try: self.server.invoke_successfully(ldap_client_create, enable_tunneling=True) except netapp_utils.zapi.NaApiError as errcatch: self.module.fail_json( msg='Error creating LDAP client %s: %s' % (self.parameters['name'], to_native(errcatch)), exception=traceback.format_exc()) def delete_ldap_client(self): ''' Delete LDAP client configuration ''' ldap_client_delete = netapp_utils.zapi.NaElement.create_node_with_children( 'ldap-client-delete', **{'ldap-client-config': self.parameters['name']}) try: self.server.invoke_successfully(ldap_client_delete, enable_tunneling=True) except netapp_utils.zapi.NaApiError as errcatch: self.module.fail_json( msg='Error deleting LDAP client configuration %s: %s' % (self.parameters['name'], to_native(errcatch)), exception=traceback.format_exc()) def modify_ldap_client(self, modify): ''' Modify LDAP client :param modify: list of modify attributes ''' ldap_client_modify = netapp_utils.zapi.NaElement('ldap-client-modify') ldap_client_modify.add_new_child('ldap-client-config', self.parameters['name']) for attribute in modify: # LDAP_servers if attribute == 'ldap_servers': ldap_servers_element = netapp_utils.zapi.NaElement( 'ldap-servers') for ldap_server_name in self.parameters['ldap_servers']: ldap_servers_element.add_new_child('string', ldap_server_name) ldap_client_modify.add_child_elem(ldap_servers_element) # preferred_ad_servers if attribute == 'preferred_ad_servers': preferred_ad_servers_element = netapp_utils.zapi.NaElement( 'preferred-ad-servers') ldap_client_modify.add_child_elem(preferred_ad_servers_element) for pref_ad_server in self.parameters['preferred_ad_servers']: preferred_ad_servers_element.add_new_child( 'ip-address', pref_ad_server) # Simple attributes if attribute in self.simple_attributes: ldap_client_modify.add_new_child( str(attribute).replace('_', '-'), str(self.parameters[attribute])) # Try to modify LDAP client try: self.server.invoke_successfully(ldap_client_modify, enable_tunneling=True) except netapp_utils.zapi.NaApiError as errcatch: self.module.fail_json( msg='Error modifying LDAP client %s: %s' % (self.parameters['name'], to_native(errcatch)), exception=traceback.format_exc()) def apply(self): '''Call create/modify/delete operations.''' current = self.get_ldap_client() cd_action = self.na_helper.get_cd_action(current, self.parameters) # state is present, either ldap_servers or ad_domain is required if self.parameters['state'] == 'present' and not self.parameters.get('ldap_servers') \ and self.parameters.get('ad_domain') is None: self.module.fail_json( msg='Required one of ldap_servers or ad_domain') if self.parameters['state'] == 'present' and cd_action is None: modify = self.na_helper.get_modified_attributes( current, self.parameters) # create an ems log event for users with auto support turned on netapp_utils.ems_log_event("na_ontap_ldap_client", self.server) if self.na_helper.changed: if self.module.check_mode: pass else: if cd_action == 'create': self.create_ldap_client() elif cd_action == 'delete': self.delete_ldap_client() elif modify: self.modify_ldap_client(modify) self.module.exit_json(changed=self.na_helper.changed)
class 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, type='str', 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', elements='str'))) self.module = AnsibleModule(argument_spec=self.argument_spec, supports_check_mode=True) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) self.set_playbook_zapi_key_map() if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi( module=self.module, vserver=self.parameters['vserver']) def set_playbook_zapi_key_map(self): self.na_helper.zapi_string_keys = {'name': 'group-name'} self.na_helper.zapi_int_keys = {'id': 'group-id'} self.na_helper.zapi_bool_keys = { 'skip_name_validation': 'skip-name-validation' } def get_unix_group(self): """ Checks if the UNIX group exists. :return: dict() if group found None if group is not found """ get_unix_group = netapp_utils.zapi.NaElement( 'name-mapping-unix-group-get-iter') attributes = { 'query': { 'unix-group-info': { 'group-name': self.parameters['name'], 'vserver': self.parameters['vserver'], } } } get_unix_group.translate_struct(attributes) try: result = self.server.invoke_successfully(get_unix_group, enable_tunneling=True) if result.get_child_by_name('num-records') and int( result.get_child_content('num-records')) >= 1: group_info = result['attributes-list']['unix-group-info'] group_details = dict() else: return None except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error getting UNIX group %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) for item_key, zapi_key in self.na_helper.zapi_string_keys.items(): group_details[item_key] = group_info[zapi_key] for item_key, zapi_key in self.na_helper.zapi_int_keys.items(): group_details[item_key] = self.na_helper.get_value_for_int( from_zapi=True, value=group_info[zapi_key]) 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 NetAppONTAPSoftwareUpdate(object): """ Class with ONTAP software update methods """ def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update(dict( state=dict(required=False, type='str', choices=['present', 'absent'], default='present'), nodes=dict(required=False, type='list', elements='str', aliases=["node"]), package_version=dict(required=True, type='str'), package_url=dict(required=True, type='str'), ignore_validation_warning=dict(required=False, type='bool', default=False), download_only=dict(required=False, type='bool', default=False), stabilize_minutes=dict(required=False, type='int'), timeout=dict(required=False, type='int', default=1800) )) self.module = AnsibleModule( argument_spec=self.argument_spec, supports_check_mode=False ) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) if HAS_NETAPP_LIB is False: self.module.fail_json(msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi(module=self.module) def cluster_image_get_iter(self): """ Compose NaElement object to query current version :return: NaElement object for cluster-image-get-iter with query """ cluster_image_get = netapp_utils.zapi.NaElement('cluster-image-get-iter') query = netapp_utils.zapi.NaElement('query') cluster_image_info = netapp_utils.zapi.NaElement('cluster-image-info') query.add_child_elem(cluster_image_info) cluster_image_get.add_child_elem(query) return cluster_image_get def cluster_image_get(self): """ Get current cluster image info :return: True if query successful, else return None """ cluster_image_get_iter = self.cluster_image_get_iter() try: result = self.server.invoke_successfully(cluster_image_get_iter, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error fetching cluster image details: %s: %s' % (self.parameters['package_version'], to_native(error)), exception=traceback.format_exc()) # return cluster image details if result.get_child_by_name('num-records') and \ int(result.get_child_content('num-records')) > 0: return True return None def cluster_image_get_for_node(self, node_name): """ Get current cluster image info for given node """ cluster_image_get = netapp_utils.zapi.NaElement('cluster-image-get') cluster_image_get.add_new_child('node-id', node_name) try: self.server.invoke_successfully(cluster_image_get, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error fetching cluster image details for %s: %s' % (node_name, to_native(error)), exception=traceback.format_exc()) @staticmethod def get_localname(tag): return netapp_utils.zapi.etree.QName(tag).localname def cluster_image_update_progress_get(self, ignore_connection_error=True): """ Get current cluster image update progress info :return: Dictionary of cluster image update progress if query successful, else return None """ cluster_update_progress_get = netapp_utils.zapi.NaElement('cluster-image-update-progress-info') cluster_update_progress_info = dict() try: result = self.server.invoke_successfully(cluster_update_progress_get, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: # return empty dict on error to satisfy package delete upon image update if ignore_connection_error: return cluster_update_progress_info self.module.fail_json(msg='Error fetching cluster image update progress details: %s' % (to_native(error)), exception=traceback.format_exc()) # return cluster image update progress details if result.get_child_by_name('attributes').get_child_by_name('ndu-progress-info'): update_progress_info = result.get_child_by_name('attributes').get_child_by_name('ndu-progress-info') cluster_update_progress_info['overall_status'] = update_progress_info.get_child_content('overall-status') cluster_update_progress_info['completed_node_count'] = update_progress_info.\ get_child_content('completed-node-count') reports = update_progress_info.get_child_by_name('validation-reports') if reports: cluster_update_progress_info['validation_reports'] = list() for report in reports.get_children(): checks = dict() for check in report.get_children(): checks[self.get_localname(check.get_name())] = check.get_content() cluster_update_progress_info['validation_reports'].append(checks) return cluster_update_progress_info def cluster_image_update(self): """ Update current cluster image """ cluster_update_info = netapp_utils.zapi.NaElement('cluster-image-update') cluster_update_info.add_new_child('package-version', self.parameters['package_version']) cluster_update_info.add_new_child('ignore-validation-warning', str(self.parameters['ignore_validation_warning'])) if self.parameters.get('stabilize_minutes'): cluster_update_info.add_new_child('stabilize-minutes', self.na_helper.get_value_for_int(False, self.parameters['stabilize_minutes'])) if self.parameters.get('nodes'): cluster_nodes = netapp_utils.zapi.NaElement('nodes') for node in self.parameters['nodes']: cluster_nodes.add_new_child('node-name', node) cluster_update_info.add_child_elem(cluster_nodes) try: self.server.invoke_successfully(cluster_update_info, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: msg = 'Error updating cluster image for %s: %s' % (self.parameters['package_version'], to_native(error)) cluster_update_progress_info = self.cluster_image_update_progress_get(ignore_connection_error=True) validation_reports = str(cluster_update_progress_info.get('validation_reports')) self.module.fail_json(msg=msg, validation_reports=validation_reports, exception=traceback.format_exc()) def cluster_image_package_download(self): """ Get current cluster image package download :return: True if package already exists, else return False """ cluster_image_package_download_info = netapp_utils.zapi.NaElement('cluster-image-package-download') cluster_image_package_download_info.add_new_child('package-url', self.parameters['package_url']) try: self.server.invoke_successfully(cluster_image_package_download_info, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: # Error 18408 denotes Package image with the same name already exists if to_native(error.code) == "18408": return True else: self.module.fail_json(msg='Error downloading cluster image package for %s: %s' % (self.parameters['package_url'], to_native(error)), exception=traceback.format_exc()) return False def cluster_image_package_delete(self): """ Delete current cluster image package """ cluster_image_package_delete_info = netapp_utils.zapi.NaElement('cluster-image-package-delete') cluster_image_package_delete_info.add_new_child('package-version', self.parameters['package_version']) try: self.server.invoke_successfully(cluster_image_package_delete_info, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error deleting cluster image package for %s: %s' % (self.parameters['package_version'], to_native(error)), exception=traceback.format_exc()) def cluster_image_package_download_progress(self): """ Get current cluster image package download progress :return: Dictionary of cluster image download progress if query successful, else return None """ cluster_image_package_download_progress_info = netapp_utils.zapi.\ NaElement('cluster-image-get-download-progress') try: result = self.server.invoke_successfully( cluster_image_package_download_progress_info, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error fetching cluster image package download progress for %s: %s' % (self.parameters['package_url'], to_native(error)), exception=traceback.format_exc()) # return cluster image download progress details cluster_download_progress_info = dict() if result.get_child_by_name('progress-status'): cluster_download_progress_info['progress_status'] = result.get_child_content('progress-status') cluster_download_progress_info['progress_details'] = result.get_child_content('progress-details') cluster_download_progress_info['failure_reason'] = result.get_child_content('failure-reason') return cluster_download_progress_info return None def autosupport_log(self): """ Autosupport log for software_update :return: """ results = netapp_utils.get_cserver(self.server) cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results) netapp_utils.ems_log_event("na_ontap_software_update", cserver) def apply(self): """ Apply action to update ONTAP software """ if self.parameters.get('https') is not True: self.module.fail_json(msg='https parameter must be True') changed = False self.autosupport_log() current = self.cluster_image_get() validation_reports = 'only available after update' if self.module.check_mode: pass else: if self.parameters.get('nodes'): for node in self.parameters['nodes']: self.cluster_image_get_for_node(node) if self.parameters.get('state') == 'present' and current: package_exists = self.cluster_image_package_download() if package_exists is False: cluster_download_progress = self.cluster_image_package_download_progress() while cluster_download_progress.get('progress_status') == 'async_pkg_get_phase_running': time.sleep(5) cluster_download_progress = self.cluster_image_package_download_progress() if cluster_download_progress.get('progress_status') == 'async_pkg_get_phase_complete': changed = True else: self.module.fail_json(msg='Error downloading package: %s' % (cluster_download_progress['failure_reason'])) if self.parameters['download_only'] is False: self.cluster_image_update() changed = True # delete package once update is completed cluster_update_progress = dict() time_left = self.parameters['timeout'] polling_interval = 25 # assume in_progress if dict is empty while time_left > 0 and cluster_update_progress.get('overall_status', 'in_progress') == 'in_progress': time.sleep(polling_interval) time_left -= polling_interval cluster_update_progress = self.cluster_image_update_progress_get(ignore_connection_error=True) if cluster_update_progress.get('overall_status') == 'completed': validation_reports = str(cluster_update_progress.get('validation_reports')) self.cluster_image_package_delete() else: cluster_update_progress = self.cluster_image_update_progress_get(ignore_connection_error=False) if cluster_update_progress.get('overall_status') != 'completed': if cluster_update_progress.get('overall_status') == 'in_progress': msg = 'Timeout error' action = ' Should the timeout value be increased? Current value is %d seconds.' % self.parameters['timeout'] action += ' The software update continues in background.' else: msg = 'Error' action = '' msg += ' updating image: overall_status: %s.' % (cluster_update_progress.get('overall_status', 'cannot get status')) msg += action validation_reports = str(cluster_update_progress.get('validation_reports')) self.module.fail_json(msg=msg, validation_reports=validation_reports) self.module.exit_json(changed=changed, validation_reports=validation_reports)