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 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 NetAppONTAPasup(object): """Class with autosupport methods""" def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update( dict( state=dict(required=False, choices=['present', 'absent'], default='present'), node_name=dict(required=True, type='str'), transport=dict(required=False, type='str', choices=['smtp', 'http', 'https']), noteto=dict(required=False, type='list'), post_url=dict(required=False, type='str'), support=dict(required=False, type='bool'), mail_hosts=dict(required=False, type='list'), from_address=dict(required=False, type='str'), partner_addresses=dict(required=False, type='list'), to_addresses=dict(required=False, type='list'), proxy_url=dict(required=False, type='str'), hostname_in_subject=dict(required=False, type='bool'), )) self.module = AnsibleModule(argument_spec=self.argument_spec, supports_check_mode=False) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) # present or absent requires modifying state to enabled or disabled self.parameters['service_state'] = 'started' if self.parameters[ 'state'] == 'present' else 'stopped' self.set_playbook_zapi_key_map() if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi(module=self.module) def set_playbook_zapi_key_map(self): self.na_helper.zapi_string_keys = { 'node_name': 'node-name', 'transport': 'transport', 'post_url': 'post-url', 'from_address': 'from', 'proxy_url': 'proxy-url' } self.na_helper.zapi_list_keys = { 'noteto': ('noteto', 'mail-address'), 'mail_hosts': ('mail-hosts', 'string'), 'partner_addresses': ('partner-address', 'mail-address'), 'to_addresses': ('to', 'mail-address'), } self.na_helper.zapi_bool_keys = { 'support': 'is-support-enabled', 'hostname_in_subject': 'is-node-in-subject' } def get_autosupport_config(self): """ Invoke zapi - get current autosupport details :return: dict() """ asup_details = netapp_utils.zapi.NaElement('autosupport-config-get') asup_details.add_new_child('node-name', self.parameters['node_name']) asup_info = dict() try: result = self.server.invoke_successfully(asup_details, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='%s' % to_native(error), exception=traceback.format_exc()) # zapi invoke successful asup_attr_info = result.get_child_by_name( 'attributes').get_child_by_name('autosupport-config-info') asup_info['service_state'] = 'started' if asup_attr_info[ 'is-enabled'] == 'true' else 'stopped' for item_key, zapi_key in self.na_helper.zapi_string_keys.items(): asup_info[item_key] = asup_attr_info[zapi_key] for item_key, zapi_key in self.na_helper.zapi_bool_keys.items(): asup_info[item_key] = self.na_helper.get_value_for_bool( from_zapi=True, value=asup_attr_info[zapi_key]) for item_key, zapi_key in self.na_helper.zapi_list_keys.items(): parent, dummy = zapi_key asup_info[item_key] = self.na_helper.get_value_for_list( from_zapi=True, zapi_parent=asup_attr_info.get_child_by_name(parent)) return asup_info def modify_autosupport_config(self, modify): """ Invoke zapi - modify autosupport config @return: NaElement object / FAILURE with an error_message """ asup_details = {'node-name': self.parameters['node_name']} if modify.get('service_state'): asup_details['is-enabled'] = 'true' if modify.get( 'service_state') == 'started' else 'false' asup_config = netapp_utils.zapi.NaElement('autosupport-config-modify') for item_key in modify: if item_key in self.na_helper.zapi_string_keys: zapi_key = self.na_helper.zapi_string_keys.get(item_key) asup_details[zapi_key] = modify[item_key] elif item_key in self.na_helper.zapi_bool_keys: zapi_key = self.na_helper.zapi_bool_keys.get(item_key) asup_details[zapi_key] = self.na_helper.get_value_for_bool( from_zapi=False, value=modify[item_key]) elif item_key in self.na_helper.zapi_list_keys: parent_key, child_key = self.na_helper.zapi_list_keys.get( item_key) asup_config.add_child_elem( self.na_helper.get_value_for_list( from_zapi=False, zapi_parent=parent_key, zapi_child=child_key, data=modify.get(item_key))) asup_config.translate_struct(asup_details) try: return self.server.invoke_successfully(asup_config, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='%s' % to_native(error), exception=traceback.format_exc()) def autosupport_log(self): results = netapp_utils.get_cserver(self.server) cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results) netapp_utils.ems_log_event("na_ontap_autosupport", cserver) def apply(self): """ Apply action to autosupport """ current = self.get_autosupport_config() modify = self.na_helper.get_modified_attributes( current, self.parameters) if self.na_helper.changed: if self.module.check_mode: pass else: self.modify_autosupport_config(modify) self.module.exit_json(changed=self.na_helper.changed)
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 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 NetAppOntapFCP(object): """ Enable and Disable FCP """ def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update(dict( state=dict(required=False, choices=['present', 'absent'], default='present'), vserver=dict(required=True, type='str'), status=dict(required=False, choices=['up', 'down'], default='up') )) 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_fcp(self): """ Create's and Starts an FCP :return: none """ try: self.server.invoke_successfully(netapp_utils.zapi.NaElement('fcp-service-create'), True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error creating FCP: %s' % (to_native(error)), exception=traceback.format_exc()) def start_fcp(self): """ Starts an existing FCP :return: none """ try: self.server.invoke_successfully(netapp_utils.zapi.NaElement('fcp-service-start'), True) except netapp_utils.zapi.NaApiError as error: # Error 13013 denotes fcp service already started. if to_native(error.code) == "13013": return None else: self.module.fail_json(msg='Error starting FCP %s' % (to_native(error)), exception=traceback.format_exc()) def stop_fcp(self): """ Steps an Existing FCP :return: none """ try: self.server.invoke_successfully(netapp_utils.zapi.NaElement('fcp-service-stop'), True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error Stopping FCP %s' % (to_native(error)), exception=traceback.format_exc()) def destroy_fcp(self): """ Destroys an already stopped FCP :return: """ try: self.server.invoke_successfully(netapp_utils.zapi.NaElement('fcp-service-destroy'), True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error destroying FCP %s' % (to_native(error)), exception=traceback.format_exc()) def get_fcp(self): fcp_obj = netapp_utils.zapi.NaElement('fcp-service-get-iter') fcp_info = netapp_utils.zapi.NaElement('fcp-service-info') fcp_info.add_new_child('vserver', self.parameters['vserver']) query = netapp_utils.zapi.NaElement('query') query.add_child_elem(fcp_info) fcp_obj.add_child_elem(query) result = self.server.invoke_successfully(fcp_obj, True) # There can only be 1 FCP per vserver. If true, one is set up, else one isn't set up if result.get_child_by_name('num-records') and \ int(result.get_child_content('num-records')) >= 1: return True else: return False def current_status(self): try: status = self.server.invoke_successfully(netapp_utils.zapi.NaElement('fcp-service-status'), True) return status.get_child_content('is-available') == 'true' except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error destroying FCP: %s' % (to_native(error)), exception=traceback.format_exc()) def apply(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_fcp", cserver) exists = self.get_fcp() changed = False if self.parameters['state'] == 'present': if exists: if self.parameters['status'] == 'up': if not self.current_status(): self.start_fcp() changed = True else: if self.current_status(): self.stop_fcp() changed = True else: self.create_fcp() if self.parameters['status'] == 'up': self.start_fcp() elif self.parameters['status'] == 'down': self.stop_fcp() changed = True else: if exists: if self.current_status(): self.stop_fcp() self.destroy_fcp() changed = True self.module.exit_json(changed=changed)
class NetAppOntapUserRole(object): def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update( dict(state=dict(required=False, choices=['present', 'absent'], default='present'), name=dict(required=True, type='str'), command_directory_name=dict(required=True, type='str'), access_level=dict(required=False, type='str', default='all', choices=['none', 'readonly', 'all']), vserver=dict(required=True, type='str'), query=dict(required=False, type='str'))) self.module = AnsibleModule(argument_spec=self.argument_spec, supports_check_mode=True) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi( module=self.module, vserver=self.parameters['vserver']) def get_role(self): """ Checks if the role exists for specific command-directory-name. :return: True if role found False if role is not found :rtype: bool """ options = { 'vserver': self.parameters['vserver'], 'role-name': self.parameters['name'], 'command-directory-name': self.parameters['command_directory_name'] } security_login_role_get_iter = netapp_utils.zapi.NaElement( 'security-login-role-get-iter') query_details = netapp_utils.zapi.NaElement.create_node_with_children( 'security-login-role-info', **options) query = netapp_utils.zapi.NaElement('query') query.add_child_elem(query_details) security_login_role_get_iter.add_child_elem(query) try: result = self.server.invoke_successfully( security_login_role_get_iter, enable_tunneling=False) except netapp_utils.zapi.NaApiError as e: # Error 16031 denotes a role not being found. if to_native(e.code) == "16031": return None # Error 16039 denotes command directory not found. elif to_native(e.code) == "16039": return None else: self.module.fail_json(msg='Error getting role %s: %s' % (self.name, to_native(e)), exception=traceback.format_exc()) if (result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1): role_info = result.get_child_by_name( 'attributes-list').get_child_by_name( 'security-login-role-info') result = { 'name': role_info['role-name'], 'access_level': role_info['access-level'], 'command_directory_name': role_info['command-directory-name'], 'query': role_info['role-query'] } return result return None def create_role(self): options = { 'vserver': self.parameters['vserver'], 'role-name': self.parameters['name'], 'command-directory-name': self.parameters['command_directory_name'], 'access-level': self.parameters['access_level'] } if self.parameters.get('query'): options['role-query'] = self.parameters['query'] role_create = netapp_utils.zapi.NaElement.create_node_with_children( 'security-login-role-create', **options) try: self.server.invoke_successfully(role_create, enable_tunneling=False) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error creating role %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def delete_role(self): role_delete = netapp_utils.zapi.NaElement.create_node_with_children( 'security-login-role-delete', **{ 'vserver': self.parameters['vserver'], 'role-name': self.parameters['name'], 'command-directory-name': self.parameters['command_directory_name'] }) try: self.server.invoke_successfully(role_delete, enable_tunneling=False) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error removing role %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def modify_role(self, modify): options = { 'vserver': self.parameters['vserver'], 'role-name': self.parameters['name'], 'command-directory-name': self.parameters['command_directory_name'] } if 'access_level' in modify.keys(): options['access-level'] = self.parameters['access_level'] if 'query' in modify.keys(): options['role-query'] = self.parameters['query'] role_modify = netapp_utils.zapi.NaElement.create_node_with_children( 'security-login-role-modify', **options) try: self.server.invoke_successfully(role_modify, enable_tunneling=False) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error modifying role %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def apply(self): self.asup_log_for_cserver('na_ontap_user_role') current = self.get_role() cd_action = self.na_helper.get_cd_action(current, self.parameters) # if desired state specify empty quote query and current query is None, set desired query to None. # otherwise na_helper.get_modified_attributes will detect a change. if self.parameters.get('query') == '' and current is not None: if current['query'] is None: self.parameters['query'] = None modify = self.na_helper.get_modified_attributes( current, self.parameters) if self.na_helper.changed: if self.module.check_mode: pass else: if cd_action == 'create': self.create_role() elif cd_action == 'delete': self.delete_role() elif modify: self.modify_role(modify) self.module.exit_json(changed=self.na_helper.changed) def asup_log_for_cserver(self, event_name): """ Fetch admin vserver for the given cluster Create and Autosupport log event with the given module name :param event_name: Name of the event log :return: None """ netapp_utils.ems_log_event(event_name, self.server)
class NetAppOntapLDAPClient(object): ''' LDAP Client definition class ''' def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update( dict(base_dn=dict(required=False, type='str'), base_scope=dict(required=False, default=None, choices=['subtree', 'onelevel', 'base']), bind_dn=dict(required=False, default=None, type='str'), bind_password=dict(type='str', required=False, default=None, no_log=True), name=dict(required=True, type='str'), ldap_servers=dict(required_if=[["state", "present"]], type='list'), min_bind_level=dict(required=False, default=None, choices=['anonymous', 'simple', 'sasl']), port=dict(required=False, default=None, type='int'), query_timeout=dict(required=False, default=None, type='int'), referral_enabled=dict(required=False, default=None, choices=['true', 'false']), schema=dict( required_if=[["state", "present"]], default=None, type='str', choices=['AD-IDMU', 'AD-SFU', 'MS-AD-BIS', 'RFC-2307']), session_security=dict(required=False, default=None, choices=['true', 'false']), state=dict(required=False, choices=['present', 'absent'], default='present'), use_start_tls=dict(required=False, default=None, choices=['true', 'false']), vserver=dict(required=True, type='str'))) self.module = AnsibleModule( argument_spec=self.argument_spec, supports_check_mode=True, required_if=[('state', 'present', ['ldap_servers', 'schema'])], ) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi( module=self.module, vserver=self.parameters['vserver']) self.simple_attributes = [ 'base_dn', 'base_scope', 'bind_dn', 'bind_password', 'min_bind_level', 'port', 'query_timeout', 'referral_enabled', 'session_security', 'use_start_tls' ] def get_ldap_client(self, client_config_name=None, vserver_name=None): ''' Checks if LDAP client config exists. :return: ldap client config object if found None if not found :rtype: object/None ''' # Make query client_config_info = netapp_utils.zapi.NaElement( 'ldap-client-get-iter') if client_config_name is None: client_config_name = self.parameters['name'] if vserver_name is None: vserver_name = '*' query_details = netapp_utils.zapi.NaElement.create_node_with_children( 'ldap-client', **{ 'ldap-client-config': client_config_name, 'vserver': vserver_name }) query = netapp_utils.zapi.NaElement('query') query.add_child_elem(query_details) client_config_info.add_child_elem(query) result = self.server.invoke_successfully(client_config_info, enable_tunneling=False) # Get LDAP client configuration details client_config_details = None if (result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1): attributes_list = result.get_child_by_name('attributes-list') client_config_info = attributes_list.get_child_by_name( 'ldap-client') # Get LDAP servers list ldap_server_list = list() get_list = client_config_info.get_child_by_name('ldap-servers') if get_list is not None: ldap_servers = get_list.get_children() for ldap_server in ldap_servers: ldap_server_list.append(ldap_server.get_content()) # Define config details structure client_config_details = { 'name': client_config_info.get_child_content('ldap-client-config'), 'ldap_servers': client_config_info.get_child_content('ldap-servers'), 'base_dn': client_config_info.get_child_content('base-dn'), 'base_scope': client_config_info.get_child_content('base-scope'), 'bind_dn': client_config_info.get_child_content('bind-dn'), 'bind_password': client_config_info.get_child_content('bind-password'), 'min_bind_level': client_config_info.get_child_content('min-bind-level'), 'port': client_config_info.get_child_content('port'), 'query_timeout': client_config_info.get_child_content('query-timeout'), 'referral_enabled': client_config_info.get_child_content('referral-enabled'), 'schema': client_config_info.get_child_content('schema'), 'session_security': client_config_info.get_child_content('session-security'), 'use_start_tls': client_config_info.get_child_content('use-start-tls'), 'vserver': client_config_info.get_child_content('vserver') } return client_config_details def create_ldap_client(self): ''' Create LDAP client configuration ''' # LDAP servers NaElement ldap_servers_element = netapp_utils.zapi.NaElement('ldap-servers') # Mandatory options for ldap_server_name in self.parameters['ldap_servers']: ldap_servers_element.add_new_child('string', ldap_server_name) options = { 'ldap-client-config': self.parameters['name'], 'schema': self.parameters['schema'], } # Other options/attributes for attribute in self.simple_attributes: if self.parameters.get(attribute) is not None: options[str(attribute).replace( '_', '-')] = self.parameters[attribute] # Initialize NaElement ldap_client_create = netapp_utils.zapi.NaElement.create_node_with_children( 'ldap-client-create', **options) ldap_client_create.add_child_elem(ldap_servers_element) # Try to create LDAP configuration try: self.server.invoke_successfully(ldap_client_create, enable_tunneling=True) except netapp_utils.zapi.NaApiError as errcatch: self.module.fail_json( msg='Error creating LDAP client %s: %s' % (self.parameters['name'], to_native(errcatch)), exception=traceback.format_exc()) def delete_ldap_client(self): ''' Delete LDAP client configuration ''' ldap_client_delete = netapp_utils.zapi.NaElement.create_node_with_children( 'ldap-client-delete', **{'ldap-client-config': self.parameters['name']}) try: self.server.invoke_successfully(ldap_client_delete, enable_tunneling=True) except netapp_utils.zapi.NaApiError as errcatch: self.module.fail_json( msg='Error deleting LDAP client configuration %s: %s' % (self.parameters['name'], to_native(errcatch)), exception=traceback.format_exc()) def modify_ldap_client(self, modify): ''' Modify LDAP client :param modify: list of modify attributes ''' ldap_client_modify = netapp_utils.zapi.NaElement('ldap-client-modify') ldap_client_modify.add_new_child('ldap-client-config', self.parameters['name']) for attribute in modify: # LDAP_servers if attribute == 'ldap_servers': ldap_servers_element = netapp_utils.zapi.NaElement( 'ldap-servers') for ldap_server_name in self.parameters['ldap_servers']: ldap_servers_element.add_new_child('string', ldap_server_name) ldap_client_modify.add_child_elem(ldap_servers_element) # Simple attributes if attribute in self.simple_attributes: ldap_client_modify.add_new_child( str(attribute).replace('_', '-'), self.parameters[attribute]) # Try to modify LDAP client try: self.server.invoke_successfully(ldap_client_modify, enable_tunneling=True) except netapp_utils.zapi.NaApiError as errcatch: self.module.fail_json( msg='Error modifying LDAP client %s: %s' % (self.parameters['name'], to_native(errcatch)), exception=traceback.format_exc()) def apply(self): '''Call create/modify/delete operations.''' current = self.get_ldap_client() cd_action = self.na_helper.get_cd_action(current, self.parameters) modify = self.na_helper.get_modified_attributes( current, self.parameters) # create an ems log event for users with auto support turned on netapp_utils.ems_log_event("na_ontap_ldap_client", self.server) if self.na_helper.changed: if self.module.check_mode: pass else: if cd_action == 'create': self.create_ldap_client() elif cd_action == 'delete': self.delete_ldap_client() elif modify: self.modify_ldap_client(modify) self.module.exit_json(changed=self.na_helper.changed)
class 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)
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 NetAppOntapVscanOnDemandTask(object): def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update( dict(state=dict(choices=['present', 'absent'], default='present'), vserver=dict(required=True, type='str'), cross_junction=dict(required=False, type='bool', default=False), directory_recursion=dict(required=False, type='bool', default=False), file_ext_to_exclude=dict(required=False, type="list"), file_ext_to_include=dict(required=False, type="list"), max_file_size=dict(required=False, type="str"), paths_to_exclude=dict(required=False, type="list"), report_directory=dict(required=False, type='str'), report_log_level=dict(required=False, choices=['verbose', 'info', 'error'], default='error'), request_timeout=dict(required=False, type='str'), scan_files_with_no_ext=dict(required=False, type='bool', default=True), scan_paths=dict(required=False, type="list"), scan_priority=dict(required=False, choices=['low', 'normal'], default='low'), schedule=dict(required=False, type="str"), task_name=dict(required=True, type="str"))) self.module = AnsibleModule(argument_spec=self.argument_spec, supports_check_mode=True, required_if=[[ "state", "present", ["report_directory", "scan_paths"] ]]) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi( module=self.module, vserver=self.parameters['vserver']) def get_demand_task(self): """ Get a demand task :return: A vscan-on-demand-task-info or None """ demand_task_iter = netapp_utils.zapi.NaElement( "vscan-on-demand-task-get-iter") demand_task_info = netapp_utils.zapi.NaElement( "vscan-on-demand-task-info") demand_task_info.add_new_child('task-name', self.parameters['task_name']) query = netapp_utils.zapi.NaElement('query') query.add_child_elem(demand_task_info) demand_task_iter.add_child_elem(query) try: result = self.server.invoke_successfully(demand_task_iter, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg='Error searching for Vscan on demand task %s: %s' % (self.parameters['task_name'], to_native(error)), exception=traceback.format_exc()) if result.get_child_by_name('num-records') and int( result.get_child_content('num-records')) >= 1: return result.get_child_by_name( 'attributes-list').get_child_by_name( 'vscan-on-demand-task-info') return None def create_demand_task(self): """ Create a Demand Task :return: None """ demand_task_obj = netapp_utils.zapi.NaElement( "vscan-on-demand-task-create") # Required items first demand_task_obj.add_new_child('report-directory', self.parameters['report_directory']) demand_task_obj.add_new_child('task-name', self.parameters['task_name']) scan_paths = netapp_utils.zapi.NaElement("scan-paths") for scan_path in self.parameters['scan_paths']: scan_paths.add_new_child('string', scan_path) demand_task_obj.add_child_elem(scan_paths) # Optional items next if self.parameters.get('cross_junction'): demand_task_obj.add_new_child( 'cross-junction', str(self.parameters['cross_junction']).lower()) if self.parameters.get('directory_recursion'): demand_task_obj.add_new_child( 'directory-recursion', str(self.parameters['directory_recursion']).lower()) if self.parameters.get('file_ext_to_exclude'): ext_to_exclude_obj = netapp_utils.zapi.NaElement( 'file-ext-to-exclude') for exclude_file in self.parameters['file_ext_to_exclude']: ext_to_exclude_obj.add_new_child('file-extension', exclude_file) demand_task_obj.add_child_elem(ext_to_exclude_obj) if self.parameters.get('file_ext_to_include'): ext_to_include_obj = netapp_utils.zapi.NaElement( 'file-ext-to-include') for include_file in self.parameters['file_ext_to_exclude']: ext_to_include_obj.add_child_elem('file-extension', include_file) demand_task_obj.add_child_elem(ext_to_include_obj) if self.parameters.get('max_file_size'): demand_task_obj.add_new_child('max-file-size', self.parameters['max_file_size']) if self.parameters.get('paths_to_exclude'): exclude_paths = netapp_utils.zapi.NaElement('paths-to-exclude') for path in self.parameters['paths_to_exclude']: exclude_paths.add_new_child('string', path) demand_task_obj.add_child_elem(exclude_paths) if self.parameters.get('report_log_level'): demand_task_obj.add_new_child('report-log-level', self.parameters['report_log_level']) if self.parameters.get('request_timeout'): demand_task_obj.add_new_child('request-timeout', self.parameters['request_timeout']) if self.parameters.get('scan_files_with_no_ext'): demand_task_obj.add_new_child( 'scan-files-with-no-ext', str(self.parameters['scan_files_with_no_ext']).lower()) if self.parameters.get('scan_priority'): demand_task_obj.add_new_child( 'scan-priority', self.parameters['scan_priority'].lower()) if self.parameters.get('schedule'): demand_task_obj.add_new_child('schedule', self.parameters['schedule']) try: result = self.server.invoke_successfully(demand_task_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg='Error creating on demand task %s: %s' % (self.parameters['task_name'], to_native(error)), exception=traceback.format_exc()) def delete_demand_task(self): """ Delete a Demand Task" :return: """ demand_task_obj = netapp_utils.zapi.NaElement( 'vscan-on-demand-task-delete') demand_task_obj.add_new_child('task-name', self.parameters['task_name']) try: self.server.invoke_successfully(demand_task_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg='Error deleting on demand task, %s: %s' % (self.parameters['task_name'], to_native(error)), exception=traceback.format_exc()) def asup_log_for_cserver(self, event_name): """ Fetch admin vserver for the given cluster Create and Autosupport log event with the given module name :param event_name: Name of the event log :return: None """ results = netapp_utils.get_cserver(self.server) cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results) netapp_utils.ems_log_event(event_name, cserver) def apply(self): self.asup_log_for_cserver("na_ontap_vscan_on_demand_task") current = self.get_demand_task() cd_action = self.na_helper.get_cd_action(current, self.parameters) if self.na_helper.changed: if self.module.check_mode: pass else: if cd_action == 'create': self.create_demand_task() elif cd_action == 'delete': self.delete_demand_task() self.module.exit_json(changed=self.na_helper.changed)
class 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) 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 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') cifs_security_details['kerberos_clock_skew'] = cifs_security_info.get_child_content('kerberos-clock-skew') cifs_security_details['kerberos_ticket_age'] = cifs_security_info.get_child_content('kerberos-ticket-age') cifs_security_details['kerberos_renew_age'] = cifs_security_info.get_child_content('kerberos-renew-age') cifs_security_details['kerberos_kdc_timeout'] = cifs_security_info.get_child_content('kerberos-kdc-timeout') cifs_security_details['is_signing_required'] = bool(cifs_security_info.get_child_content('is-signing-required')) cifs_security_details['is_password_complexity_required'] = bool(cifs_security_info.get_child_content('is-password-complexity-required')) cifs_security_details['is_aes_encryption_enabled'] = bool(cifs_security_info.get_child_content('is-aes-encryption-enabled')) cifs_security_details['is_smb_encryption_required'] = bool(cifs_security_info.get_child_content('is-smb-encryption-required')) cifs_security_details['lm_compatibility_level'] = cifs_security_info.get_child_content('lm-compatibility-level') cifs_security_details['referral_enabled_for_ad_ldap'] = bool(cifs_security_info.get_child_content('referral-enabled-for-ad-ldap')) cifs_security_details['session_security_for_ad_ldap'] = cifs_security_info.get_child_content('session-security-for-ad-ldap') cifs_security_details['smb1_enabled_for_dc_connections'] = cifs_security_info.get_child_content('smb1-enabled-for-dc-connections') cifs_security_details['smb2_enabled_for_dc_connections'] = cifs_security_info.get_child_content('smb2-enabled-for-dc-connections') cifs_security_details['use_start_tls_for_ad_ldap'] = bool(cifs_security_info.get_child_content('use-start-tls-for-ad-ldap')) 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 NetAppOntapLUNCopy(object): def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update(dict( state=dict(required=False, choices=['present'], default='present'), destination_vserver=dict(required=True, type='str'), destination_path=dict(required=True, type='str'), source_path=dict(required=True, type='str'), source_vserver=dict(required=False, type='str'), )) self.module = AnsibleModule( argument_spec=self.argument_spec, supports_check_mode=True ) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) if HAS_NETAPP_LIB is False: self.module.fail_json(msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['destination_vserver']) def get_lun(self): """ Check if the LUN exists :return: true is it exists, false otherwise :rtype: bool """ return_value = False lun_info = netapp_utils.zapi.NaElement('lun-get-iter') query_details = netapp_utils.zapi.NaElement('lun-info') query_details.add_new_child('path', self.parameters['destination_path']) query_details.add_new_child('vserver', self.parameters['destination_vserver']) query = netapp_utils.zapi.NaElement('query') query.add_child_elem(query_details) lun_info.add_child_elem(query) try: result = self.server.invoke_successfully(lun_info, True) except netapp_utils.zapi.NaApiError as e: self.module.fail_json(msg="Error getting lun info %s for verver %s: %s" % (self.parameters['destination_path'], self.parameters['destination_vserver'], to_native(e)), exception=traceback.format_exc()) if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1: return_value = True return return_value def copy_lun(self): """ Copy LUN with requested path and vserver """ lun_copy = netapp_utils.zapi.NaElement.create_node_with_children( 'lun-copy-start', **{'source-vserver': self.parameters['source_vserver']}) path_obj = netapp_utils.zapi.NaElement('paths') pair = netapp_utils.zapi.NaElement('lun-path-pair') pair.add_new_child('destination-path', self.parameters['destination_path']) pair.add_new_child('source-path', self.parameters['source_path']) path_obj.add_child_elem(pair) lun_copy.add_child_elem(path_obj) try: self.server.invoke_successfully(lun_copy, enable_tunneling=True) except netapp_utils.zapi.NaApiError as e: self.module.fail_json(msg="Error copying lun from %s to vserver %s: %s" % (self.parameters['source_vserver'], self.parameters['destination_vserver'], to_native(e)), exception=traceback.format_exc()) def apply(self): netapp_utils.ems_log_event("na_ontap_lun_copy", self.server) if self.get_lun(): # lun already exists at destination changed = False else: changed = True if self.module.check_mode: pass else: # need to copy lun if self.parameters['state'] == 'present': self.copy_lun() self.module.exit_json(changed=changed)
class NetAppOntapUnixUser(object): """ Common operations to manage users and roles. """ def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update( dict( state=dict(required=False, choices=['present', 'absent'], default='present'), name=dict(required=True, type='str'), group_id=dict(required=False, type='int'), id=dict(required=False, type='int'), full_name=dict(required=False, type='str'), vserver=dict(required=True, type='str'), )) self.module = AnsibleModule(argument_spec=self.argument_spec, supports_check_mode=True) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi( module=self.module, vserver=self.parameters['vserver']) def get_unix_user(self): """ Checks if the UNIX user exists. :return: dict() if user found None if user is not found """ get_unix_user = netapp_utils.zapi.NaElement( 'name-mapping-unix-user-get-iter') attributes = { 'query': { 'unix-user-info': { 'user-name': self.parameters['name'], 'vserver': self.parameters['vserver'], } } } get_unix_user.translate_struct(attributes) try: result = self.server.invoke_successfully(get_unix_user, enable_tunneling=True) if result.get_child_by_name('num-records') and int( result.get_child_content('num-records')) >= 1: user_info = result['attributes-list']['unix-user-info'] return { 'group_id': int(user_info['group-id']), 'id': int(user_info['user-id']), 'full_name': user_info['full-name'] } return None except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error getting UNIX user %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def create_unix_user(self): """ Creates an UNIX user in the specified Vserver :return: None """ if self.parameters.get('group_id') is None or self.parameters.get( 'id') is None: self.module.fail_json( msg= 'Error: Missing one or more required parameters for create: (group_id, id)' ) user_create = netapp_utils.zapi.NaElement.create_node_with_children( 'name-mapping-unix-user-create', **{ 'user-name': self.parameters['name'], 'group-id': str(self.parameters['group_id']), 'user-id': str(self.parameters['id']) }) if self.parameters.get('full_name') is not None: user_create.add_new_child('full-name', self.parameters['full_name']) try: self.server.invoke_successfully(user_create, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error creating UNIX user %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def delete_unix_user(self): """ Deletes an UNIX user from a vserver :return: None """ user_delete = netapp_utils.zapi.NaElement.create_node_with_children( 'name-mapping-unix-user-destroy', **{'user-name': self.parameters['name']}) try: self.server.invoke_successfully(user_delete, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error removing UNIX user %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def modify_unix_user(self, params): user_modify = netapp_utils.zapi.NaElement.create_node_with_children( 'name-mapping-unix-user-modify', **{'user-name': self.parameters['name']}) for key in params: if key == 'group_id': user_modify.add_new_child('group-id', str(params['group_id'])) if key == 'id': user_modify.add_new_child('user-id', str(params['id'])) if key == 'full_name': user_modify.add_new_child('full-name', params['full_name']) try: self.server.invoke_successfully(user_modify, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error modifying UNIX user %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def autosupport_log(self): """ Autosupport log for unix_user :return: None """ netapp_utils.ems_log_event("na_ontap_unix_user", self.server) def apply(self): """ Invoke appropriate action based on playbook parameters :return: None """ self.autosupport_log() current = self.get_unix_user() cd_action = self.na_helper.get_cd_action(current, self.parameters) if self.parameters['state'] == 'present' and cd_action is None: modify = self.na_helper.get_modified_attributes( current, self.parameters) if self.na_helper.changed: if self.module.check_mode: pass else: if cd_action == 'create': self.create_unix_user() elif cd_action == 'delete': self.delete_unix_user() else: self.modify_unix_user(modify) self.module.exit_json(changed=self.na_helper.changed)
class NetAppONTAPQuotas(object): '''Class with quotas methods''' def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update(dict( state=dict(required=False, choices=['present', 'absent'], default='present'), vserver=dict(required=True, type='str'), volume=dict(required=True, type='str'), quota_target=dict(required=True, type='str'), qtree=dict(required=False, type='str', default=""), type=dict(required=True, type='str', choices=['user', 'group', 'tree']), policy=dict(required=False, type='str'), set_quota_status=dict(required=False, type='bool'), file_limit=dict(required=False, type='str', default='-'), disk_limit=dict(required=False, type='str', default='-'), threshold=dict(required=False, type='str', default='-') )) self.module = AnsibleModule( argument_spec=self.argument_spec, supports_check_mode=True ) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=self.parameters['vserver']) def get_quota_status(self): """ Return details about the quota status :param: name : volume name :return: status of the quota. None if not found. :rtype: dict """ quota_status_get = netapp_utils.zapi.NaElement('quota-status') quota_status_get.translate_struct({ 'volume': self.parameters['volume'] }) try: result = self.server.invoke_successfully(quota_status_get, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error fetching quotas status info: %s' % to_native(error), exception=traceback.format_exc()) if result: return result['status'] return None def get_quotas(self): """ Get quota details :return: name of volume if quota exists, None otherwise """ quota_get = netapp_utils.zapi.NaElement('quota-list-entries-iter') query = { 'query': { 'quota-entry': { 'volume': self.parameters['volume'], 'quota-target': self.parameters['quota_target'], 'quota-type': self.parameters['type'], 'vserver': self.parameters['vserver'] } } } quota_get.translate_struct(query) if self.parameters.get('policy'): quota_get['query']['quota-entry'].add_new_child('policy', self.parameters['policy']) try: result = self.server.invoke_successfully(quota_get, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error fetching quotas info: %s' % to_native(error), exception=traceback.format_exc()) if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1: return_values = {'volume': result['attributes-list']['quota-entry']['volume'], 'file_limit': result['attributes-list']['quota-entry']['file-limit'], 'disk_limit': result['attributes-list']['quota-entry']['disk-limit'], 'threshold': result['attributes-list']['quota-entry']['threshold']} return return_values return None def quota_entry_set(self): """ Adds a quota entry """ options = {'volume': self.parameters['volume'], 'quota-target': self.parameters['quota_target'], 'quota-type': self.parameters['type'], 'qtree': self.parameters['qtree'], 'file-limit': self.parameters['file_limit'], 'disk-limit': self.parameters['disk_limit'], 'threshold': self.parameters['threshold']} if self.parameters.get('policy'): options['policy'] = self.parameters['policy'] set_entry = netapp_utils.zapi.NaElement.create_node_with_children( 'quota-set-entry', **options) try: self.server.invoke_successfully(set_entry, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error adding/modifying quota entry %s: %s' % (self.parameters['volume'], to_native(error)), exception=traceback.format_exc()) def quota_entry_delete(self): """ Deletes a quota entry """ options = {'volume': self.parameters['volume'], 'quota-target': self.parameters['quota_target'], 'quota-type': self.parameters['type'], 'qtree': self.parameters['qtree']} set_entry = netapp_utils.zapi.NaElement.create_node_with_children( 'quota-delete-entry', **options) if self.parameters.get('policy'): set_entry.add_new_child('policy', self.parameters['policy']) try: self.server.invoke_successfully(set_entry, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error deleting quota entry %s: %s' % (self.parameters['volume'], to_native(error)), exception=traceback.format_exc()) def quota_entry_modify(self, modify_attrs): """ Modifies a quota entry """ options = {'volume': self.parameters['volume'], 'quota-target': self.parameters['quota_target'], 'quota-type': self.parameters['type'], 'qtree': self.parameters['qtree']} options.update(modify_attrs) if self.parameters.get('policy'): options['policy'] = str(self.parameters['policy']) modify_entry = netapp_utils.zapi.NaElement.create_node_with_children( 'quota-modify-entry', **options) try: self.server.invoke_successfully(modify_entry, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error modifying quota entry %s: %s' % (self.parameters['volume'], to_native(error)), exception=traceback.format_exc()) def on_or_off_quota(self, status): """ on or off quota """ quota = netapp_utils.zapi.NaElement.create_node_with_children( status, **{'volume': self.parameters['volume']}) try: self.server.invoke_successfully(quota, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error setting %s for %s: %s' % (status, self.parameters['volume'], to_native(error)), exception=traceback.format_exc()) def apply(self): """ Apply action to quotas """ netapp_utils.ems_log_event("na_ontap_quotas", self.server) modify_quota_status = None modify_quota = None current = self.get_quotas() if 'set_quota_status' in self.parameters: quota_status = self.get_quota_status() if quota_status is not None: quota_status_action = self.na_helper.get_modified_attributes( {'set_quota_status': True if quota_status == 'on' else False}, self.parameters) if quota_status_action: modify_quota_status = 'quota-on' if quota_status_action['set_quota_status'] else 'quota-off' cd_action = self.na_helper.get_cd_action(current, self.parameters) if cd_action is None: modify_quota = self.na_helper.get_modified_attributes(current, self.parameters) if self.na_helper.changed: if self.module.check_mode: pass else: if cd_action == 'create': self.quota_entry_set() elif cd_action == 'delete': self.quota_entry_delete() elif modify_quota is not None: for key in list(modify_quota): modify_quota[key.replace("_", "-")] = modify_quota.pop(key) self.quota_entry_modify(modify_quota) if modify_quota_status is not None: self.on_or_off_quota(modify_quota_status) self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapQTree(object): '''Class with qtree operations''' def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update( dict( state=dict(required=False, choices=['present', 'absent'], default='present'), name=dict(required=True, type='str'), from_name=dict(required=False, type='str'), flexvol_name=dict(type='str'), vserver=dict(required=True, type='str'), export_policy=dict(required=False, type='str'), security_style=dict(required=False, choices=['unix', 'ntfs', 'mixed']), oplocks=dict(required=False, choices=['enabled', 'disabled']), unix_permissions=dict(required=False, type='str'), )) self.module = AnsibleModule(argument_spec=self.argument_spec, required_if=[('state', 'present', ['flexvol_name'])], supports_check_mode=True) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi( module=self.module, vserver=self.parameters['vserver']) def get_qtree(self, name=None): """ Checks if the qtree exists. :param: name : qtree name :return: Details about the qtree False if qtree is not found :rtype: bool """ if name is None: name = self.parameters['name'] qtree_list_iter = netapp_utils.zapi.NaElement('qtree-list-iter') query_details = netapp_utils.zapi.NaElement.create_node_with_children( 'qtree-info', **{ 'vserver': self.parameters['vserver'], 'volume': self.parameters['flexvol_name'], 'qtree': name }) query = netapp_utils.zapi.NaElement('query') query.add_child_elem(query_details) qtree_list_iter.add_child_elem(query) result = self.server.invoke_successfully(qtree_list_iter, enable_tunneling=True) return_q = None if (result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1): return_q = { 'export_policy': result['attributes-list']['qtree-info']['export-policy'], 'unix_permissions': result['attributes-list']['qtree-info']['mode'], 'oplocks': result['attributes-list']['qtree-info']['oplocks'], 'security_style': result['attributes-list']['qtree-info']['security-style'] } return return_q def create_qtree(self): """ Create a qtree """ options = { 'qtree': self.parameters['name'], 'volume': self.parameters['flexvol_name'] } if self.parameters.get('export_policy'): options['export-policy'] = self.parameters['export_policy'] if self.parameters.get('security_style'): options['security-style'] = self.parameters['security_style'] if self.parameters.get('oplocks'): options['oplocks'] = self.parameters['oplocks'] if self.parameters.get('unix_permissions'): options['mode'] = self.parameters['unix_permissions'] qtree_create = netapp_utils.zapi.NaElement.create_node_with_children( 'qtree-create', **options) try: self.server.invoke_successfully(qtree_create, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg="Error provisioning qtree %s: %s" % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def delete_qtree(self): """ Delete a qtree """ path = '/vol/%s/%s' % (self.parameters['flexvol_name'], self.parameters['name']) qtree_delete = netapp_utils.zapi.NaElement.create_node_with_children( 'qtree-delete', **{'qtree': path}) try: self.server.invoke_successfully(qtree_delete, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg="Error deleting qtree %s: %s" % (path, to_native(error)), exception=traceback.format_exc()) def rename_qtree(self): """ Rename a qtree """ path = '/vol/%s/%s' % (self.parameters['flexvol_name'], self.parameters['from_name']) new_path = '/vol/%s/%s' % (self.parameters['flexvol_name'], self.parameters['name']) qtree_rename = netapp_utils.zapi.NaElement.create_node_with_children( 'qtree-rename', **{ 'qtree': path, 'new-qtree-name': new_path }) try: self.server.invoke_successfully(qtree_rename, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg="Error renaming qtree %s: %s" % (self.parameters['from_name'], to_native(error)), exception=traceback.format_exc()) def modify_qtree(self): """ Modify a qtree """ options = { 'qtree': self.parameters['name'], 'volume': self.parameters['flexvol_name'] } if self.parameters.get('export_policy'): options['export-policy'] = self.parameters['export_policy'] if self.parameters.get('security_style'): options['security-style'] = self.parameters['security_style'] if self.parameters.get('oplocks'): options['oplocks'] = self.parameters['oplocks'] if self.parameters.get('unix_permissions'): options['mode'] = self.parameters['unix_permissions'] qtree_modify = netapp_utils.zapi.NaElement.create_node_with_children( 'qtree-modify', **options) try: self.server.invoke_successfully(qtree_modify, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error modifying qtree %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def apply(self): '''Call create/delete/modify/rename operations''' netapp_utils.ems_log_event("na_ontap_qtree", self.server) current = self.get_qtree() rename, cd_action, modify = None, None, None if self.parameters.get('from_name'): rename = self.na_helper.is_rename_action( self.get_qtree(self.parameters['from_name']), current) else: cd_action = self.na_helper.get_cd_action(current, self.parameters) if cd_action is None and self.parameters['state'] == 'present': modify = self.na_helper.get_modified_attributes( current, self.parameters) if self.na_helper.changed: if self.module.check_mode: pass else: if rename: self.rename_qtree() if cd_action == 'create': self.create_qtree() elif cd_action == 'delete': self.delete_qtree() elif modify: self.modify_qtree() self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapNetPort(object): """ Modify a Net port """ def __init__(self): """ Initialize the Ontap Net Port Class """ self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update(dict( state=dict(required=False, choices=['present'], default='present'), node=dict(required=True, type="str"), ports=dict(required=True, type="list", aliases=['port']), mtu=dict(required=False, type="str", default=None), autonegotiate_admin=dict(required=False, type="str", default=None), duplex_admin=dict(required=False, type="str", default=None), speed_admin=dict(required=False, type="str", default=None), flowcontrol_admin=dict(required=False, type="str", default=None), ipspace=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) 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) return def set_playbook_zapi_key_map(self): self.na_helper.zapi_string_keys = { 'mtu': 'mtu', 'autonegotiate_admin': 'is-administrative-auto-negotiate', 'duplex_admin': 'administrative-duplex', 'speed_admin': 'administrative-speed', 'flowcontrol_admin': 'administrative-flowcontrol', 'ipspace': 'ipspace' } def get_net_port(self, port): """ Return details about the net port :param: port: Name of the port :return: Dictionary with current state of the port. None if not found. :rtype: dict """ net_port_get = netapp_utils.zapi.NaElement('net-port-get-iter') attributes = { 'query': { 'net-port-info': { 'node': self.parameters['node'], 'port': port } } } net_port_get.translate_struct(attributes) try: result = self.server.invoke_successfully(net_port_get, True) if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1: port_info = result['attributes-list']['net-port-info'] port_details = dict() else: return None except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error getting net ports for %s: %s' % (self.parameters['node'], to_native(error)), exception=traceback.format_exc()) for item_key, zapi_key in self.na_helper.zapi_string_keys.items(): port_details[item_key] = port_info.get_child_content(zapi_key) return port_details def modify_net_port(self, port, modify): """ Modify a port :param port: Name of the port :param modify: dict with attributes to be modified :return: None """ port_modify = netapp_utils.zapi.NaElement('net-port-modify') port_attributes = {'node': self.parameters['node'], 'port': port} for key in modify: if key in self.na_helper.zapi_string_keys: zapi_key = self.na_helper.zapi_string_keys.get(key) port_attributes[zapi_key] = modify[key] port_modify.translate_struct(port_attributes) try: self.server.invoke_successfully(port_modify, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error modifying net ports for %s: %s' % (self.parameters['node'], to_native(error)), exception=traceback.format_exc()) def autosupport_log(self): """ AutoSupport log for na_ontap_net_port :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_net_port", cserver) def apply(self): """ Run Module based on play book """ self.autosupport_log() # Run the task for all ports in the list of 'ports' for port in self.parameters['ports']: current = self.get_net_port(port) 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.modify_net_port(port, modify) self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapUser(object): """ Common operations to manage users and roles. """ def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update( dict( state=dict(required=False, choices=['present', 'absent'], default='present'), name=dict(required=True, type='str'), applications=dict( required=True, type='list', aliases=['application'], choices=[ 'console', 'http', 'ontapi', 'rsh', 'snmp', 'sp', 'service-processor', 'ssh', 'telnet' ], ), authentication_method=dict(required=True, type='str', choices=[ 'community', 'password', 'publickey', 'domain', 'nsswitch', 'usm', 'cert' ]), set_password=dict(required=False, type='str', no_log=True), role_name=dict(required=False, type='str'), lock_user=dict(required=False, type='bool'), vserver=dict(required=True, type='str'), )) self.module = AnsibleModule(argument_spec=self.argument_spec, required_if=[('state', 'present', ['role_name'])], supports_check_mode=True) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi( module=self.module, vserver=self.parameters['vserver']) def get_user(self, application=None): """ Checks if the user exists. :param: application: application to grant access to :return: Dictionary if user found None if user is not found """ security_login_get_iter = netapp_utils.zapi.NaElement( 'security-login-get-iter') query_details = netapp_utils.zapi.NaElement.create_node_with_children( 'security-login-account-info', **{ 'vserver': self.parameters['vserver'], 'user-name': self.parameters['name'], 'authentication-method': self.parameters['authentication_method'] }) if application is not None: query_details.add_new_child('application', application) query = netapp_utils.zapi.NaElement('query') query.add_child_elem(query_details) security_login_get_iter.add_child_elem(query) try: result = self.server.invoke_successfully(security_login_get_iter, enable_tunneling=False) if result.get_child_by_name('num-records') and \ int(result.get_child_content('num-records')) >= 1: interface_attributes = result.get_child_by_name('attributes-list').\ get_child_by_name('security-login-account-info') return_value = { 'lock_user': interface_attributes.get_child_content('is-locked'), 'role_name': interface_attributes.get_child_content('role-name') } return return_value return None except netapp_utils.zapi.NaApiError as error: # Error 16034 denotes a user not being found. if to_native(error.code) == "16034": return None # Error 16043 denotes the user existing, but the application missing elif to_native(error.code) == "16043": return None else: self.module.fail_json( msg='Error getting user %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def create_user(self, application): """ creates the user for the given application and authentication_method :param: application: application to grant access to """ user_create = netapp_utils.zapi.NaElement.create_node_with_children( 'security-login-create', **{ 'vserver': self.parameters['vserver'], 'user-name': self.parameters['name'], 'application': application, 'authentication-method': self.parameters['authentication_method'], 'role-name': self.parameters.get('role_name') }) if self.parameters.get('set_password') is not None: user_create.add_new_child('password', self.parameters.get('set_password')) try: self.server.invoke_successfully(user_create, enable_tunneling=False) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error creating user %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def lock_given_user(self): """ locks the user :return: True if user locked False if lock user is not performed :rtype: bool """ user_lock = netapp_utils.zapi.NaElement.create_node_with_children( 'security-login-lock', **{ 'vserver': self.parameters['vserver'], 'user-name': self.parameters['name'] }) try: self.server.invoke_successfully(user_lock, enable_tunneling=False) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error locking user %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def unlock_given_user(self): """ unlocks the user :return: True if user unlocked False if unlock user is not performed :rtype: bool """ user_unlock = netapp_utils.zapi.NaElement.create_node_with_children( 'security-login-unlock', **{ 'vserver': self.parameters['vserver'], 'user-name': self.parameters['name'] }) try: self.server.invoke_successfully(user_unlock, enable_tunneling=False) except netapp_utils.zapi.NaApiError as error: if to_native(error.code) == '13114': return False else: self.module.fail_json( msg='Error unlocking user %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) return True def delete_user(self, application): """ deletes the user for the given application and authentication_method :param: application: application to grant access to """ user_delete = netapp_utils.zapi.NaElement.create_node_with_children( 'security-login-delete', **{ 'vserver': self.parameters['vserver'], 'user-name': self.parameters['name'], 'application': application, 'authentication-method': self.parameters['authentication_method'] }) try: self.server.invoke_successfully(user_delete, enable_tunneling=False) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error removing user %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def change_password(self): """ Changes the password :return: True if password updated False if password is not updated :rtype: bool """ # self.server.set_vserver(self.parameters['vserver']) modify_password = netapp_utils.zapi.NaElement.create_node_with_children( 'security-login-modify-password', **{ 'new-password': str(self.parameters.get('set_password')), 'user-name': self.parameters['name'] }) try: self.server.invoke_successfully(modify_password, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: if to_native(error.code) == '13114': return False # if the user give the same password, instead of returning an error, return ok if to_native(error.code) == '13214' and \ (error.message.startswith('New password must be different than last 6 passwords.') or error.message.startswith('New password must be different than the old password.')): return False self.module.fail_json( msg='Error setting password for user %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) self.server.set_vserver(None) return True def modify_user(self, application): """ Modify user """ user_modify = netapp_utils.zapi.NaElement.create_node_with_children( 'security-login-modify', **{ 'vserver': self.parameters['vserver'], 'user-name': self.parameters['name'], 'application': application, 'authentication-method': self.parameters['authentication_method'], 'role-name': self.parameters.get('role_name') }) try: self.server.invoke_successfully(user_modify, enable_tunneling=False) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error modifying user %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def apply(self): create_delete_decision = {} modify_decision = {} netapp_utils.ems_log_event("na_ontap_user", self.server) for application in self.parameters['applications']: current = self.get_user(application) if current is not None: current['lock_user'] = self.na_helper.get_value_for_bool( True, current['lock_user']) cd_action = self.na_helper.get_cd_action(current, self.parameters) if cd_action is not None: create_delete_decision[application] = cd_action else: modify_decision[ application] = self.na_helper.get_modified_attributes( current, self.parameters) if not create_delete_decision and self.parameters.get( 'state') == 'present': if self.parameters.get('set_password') is not None: self.na_helper.changed = True if self.na_helper.changed: if self.module.check_mode: pass else: for application in create_delete_decision: if create_delete_decision[application] == 'create': self.create_user(application) elif create_delete_decision[application] == 'delete': self.delete_user(application) lock_user = False for application in modify_decision: if 'role_name' in modify_decision[application]: self.modify_user(application) if 'lock_user' in modify_decision[application]: lock_user = True if lock_user: if self.parameters.get('lock_user'): self.lock_given_user() else: self.unlock_given_user() if not create_delete_decision and self.parameters.get( 'set_password') is not None: # if change password return false nothing has changed so we need to set changed to False self.na_helper.changed = self.change_password() self.module.exit_json(changed=self.na_helper.changed)
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 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)
class NetAppONTAPNVMe(object): """ Class with NVMe service methods """ def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update( dict(state=dict(required=False, type='str', choices=['present', 'absent'], default='present'), vserver=dict(required=True, type='str'), status_admin=dict(required=False, type='bool'))) self.module = AnsibleModule(argument_spec=self.argument_spec, supports_check_mode=True) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi( module=self.module, vserver=self.parameters['vserver']) def get_nvme(self): """ Get current nvme details :return: dict if nvme exists, None otherwise """ nvme_get = netapp_utils.zapi.NaElement('nvme-get-iter') query = { 'query': { 'nvme-target-service-info': { 'vserver': self.parameters['vserver'] } } } nvme_get.translate_struct(query) try: result = self.server.invoke_successfully(nvme_get, enable_tunneling=False) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error fetching nvme info: %s' % to_native(error), exception=traceback.format_exc()) if result.get_child_by_name('num-records') and int( result.get_child_content('num-records')) >= 1: attributes_list = result.get_child_by_name('attributes-list') nvme_info = attributes_list.get_child_by_name( 'nvme-target-service-info') return_value = { 'status_admin': nvme_info.get_child_content('is-available') } return return_value return None def create_nvme(self): """ Create NVMe service """ nvme_create = netapp_utils.zapi.NaElement('nvme-create') if self.parameters.get('status_admin') is not None: options = {'is-available': self.parameters['status_admin']} nvme_create.translate_struct(options) try: self.server.invoke_successfully(nvme_create, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg='Error creating nvme for vserver %s: %s' % (self.parameters['vserver'], to_native(error)), exception=traceback.format_exc()) def delete_nvme(self): """ Delete NVMe service """ nvme_delete = netapp_utils.zapi.NaElement('nvme-delete') try: self.server.invoke_successfully(nvme_delete, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg='Error deleting nvme for vserver %s: %s' % (self.parameters['vserver'], to_native(error)), exception=traceback.format_exc()) def modify_nvme(self, status=None): """ Modify NVMe service """ if status is None: status = self.parameters['status_admin'] options = {'is-available': status} nvme_modify = netapp_utils.zapi.NaElement('nvme-modify') nvme_modify.translate_struct(options) try: self.server.invoke_successfully(nvme_modify, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg='Error modifying nvme for vserver %s: %s' % (self.parameters['vserver'], to_native(error)), exception=traceback.format_exc()) def apply(self): """ Apply action to NVMe service """ netapp_utils.ems_log_event("na_ontap_nvme", self.server) current = self.get_nvme() cd_action = self.na_helper.get_cd_action(current, self.parameters) if self.parameters.get('status_admin') is not None: self.parameters[ 'status_admin'] = self.na_helper.get_value_for_bool( False, self.parameters['status_admin']) if cd_action is None and self.parameters['state'] == 'present': modify = self.na_helper.get_modified_attributes( current, self.parameters) if self.na_helper.changed: if self.module.check_mode: pass else: if cd_action == 'create': self.create_nvme() elif cd_action == 'delete': # NVMe status_admin needs to be down before deleting it self.modify_nvme('false') self.delete_nvme() elif modify: self.modify_nvme() self.module.exit_json(changed=self.na_helper.changed)
class NetAppONTAPPortset(object): """ Methods to create or delete portset """ def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update( dict(state=dict(required=False, default='present'), vserver=dict(required=True, type='str'), name=dict(required=True, type='str'), type=dict(required=False, type='str', choices=['fcp', 'iscsi', 'mixed']), force=dict(required=False, type='bool', default=False), ports=dict(required=False, type='list'))) self.module = AnsibleModule(argument_spec=self.argument_spec, supports_check_mode=True) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi( module=self.module, vserver=self.parameters['vserver']) def portset_get_iter(self): """ Compose NaElement object to query current portset using vserver, portset-name and portset-type parameters :return: NaElement object for portset-get-iter with query """ portset_get = netapp_utils.zapi.NaElement('portset-get-iter') query = netapp_utils.zapi.NaElement('query') portset_info = netapp_utils.zapi.NaElement('portset-info') portset_info.add_new_child('vserver', self.parameters['vserver']) portset_info.add_new_child('portset-name', self.parameters['name']) if self.parameters.get('type'): portset_info.add_new_child('portset-type', self.parameters['type']) query.add_child_elem(portset_info) portset_get.add_child_elem(query) return portset_get def portset_get(self): """ Get current portset info :return: Dictionary of current portset details if query successful, else return None """ portset_get_iter = self.portset_get_iter() result, portset_info = None, dict() try: result = self.server.invoke_successfully(portset_get_iter, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error fetching portset %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) # return portset details if result.get_child_by_name('num-records') and int( result.get_child_content('num-records')) > 0: portset_get_info = result.get_child_by_name( 'attributes-list').get_child_by_name('portset-info') if int(portset_get_info.get_child_content( 'portset-port-total')) > 0: ports = portset_get_info.get_child_by_name('portset-port-info') portset_info['ports'] = [ port.get_content() for port in ports.get_children() ] else: portset_info['ports'] = [] return portset_info return None def create_portset(self): """ Create a portset """ if self.parameters.get('type') is None: self.module.fail_json( msg='Error: Missing required parameter for create (type)') portset_info = netapp_utils.zapi.NaElement("portset-create") portset_info.add_new_child("portset-name", self.parameters['name']) portset_info.add_new_child("portset-type", self.parameters['type']) try: self.server.invoke_successfully(portset_info, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg="Error creating portset %s: %s" % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def delete_portset(self): """ Delete a portset """ portset_info = netapp_utils.zapi.NaElement("portset-destroy") portset_info.add_new_child("portset-name", self.parameters['name']) if self.parameters.get('force'): portset_info.add_new_child("force", str(self.parameters['force'])) try: self.server.invoke_successfully(portset_info, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg="Error deleting portset %s: %s" % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def remove_ports(self, ports): """ Removes all existing ports from portset :return: None """ for port in ports: self.modify_port(port, 'portset-remove', 'removing') def add_ports(self): """ Add the list of ports to portset :return: None """ # don't add if ports is empty string if self.parameters.get('ports') == [ '' ] or self.parameters.get('ports') is None: return for port in self.parameters['ports']: self.modify_port(port, 'portset-add', 'adding') def modify_port(self, port, zapi, action): """ Add or remove an port to/from a portset """ port.strip( ) # remove leading spaces if any (eg: if user types a space after comma in initiators list) options = { 'portset-name': self.parameters['name'], 'portset-port-name': port } portset_modify = netapp_utils.zapi.NaElement.create_node_with_children( zapi, **options) try: self.server.invoke_successfully(portset_modify, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg='Error %s port in portset %s: %s' % (action, self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def apply(self): """ Applies action from playbook """ netapp_utils.ems_log_event("na_ontap_autosupport", self.server) current, modify = self.portset_get(), None cd_action = self.na_helper.get_cd_action(current, self.parameters) if cd_action is None and self.parameters['state'] == 'present': modify = self.na_helper.get_modified_attributes( current, self.parameters) if self.na_helper.changed: if self.module.check_mode: pass else: if cd_action == 'create': self.create_portset() self.add_ports() elif cd_action == 'delete': self.delete_portset() elif modify: self.remove_ports(current['ports']) self.add_ports() self.module.exit_json(changed=self.na_helper.changed)
class NetAppontapExportRule(object): ''' object initialize and class methods ''' def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update(dict( state=dict(required=False, type='str', choices=['present', 'absent'], default='present'), name=dict(required=True, type='str', aliases=['policy_name']), protocol=dict(required=False, type='list', default=None, choices=['any', 'nfs', 'nfs3', 'nfs4', 'cifs', 'flexcache']), client_match=dict(required=False, type='list'), ro_rule=dict(required=False, type='list', default=None, choices=['any', 'none', 'never', 'krb5', 'krb5i', 'krb5p', 'ntlm', 'sys']), rw_rule=dict(required=False, type='list', default=None, choices=['any', 'none', 'never', 'krb5', 'krb5i', 'krb5p', 'ntlm', 'sys']), super_user_security=dict(required=False, type='list', default=None, choices=['any', 'none', 'never', 'krb5', 'krb5i', 'krb5p', 'ntlm', 'sys']), allow_suid=dict(required=False, type='bool'), rule_index=dict(required=False, type='int'), vserver=dict(required=True, type='str'), )) self.module = AnsibleModule( argument_spec=self.argument_spec, supports_check_mode=True ) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) self.set_playbook_zapi_key_map() if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi( module=self.module, vserver=self.parameters['vserver']) def set_playbook_zapi_key_map(self): self.na_helper.zapi_string_keys = { 'client_match': 'client-match', 'name': 'policy-name' } self.na_helper.zapi_list_keys = { 'protocol': ('protocol', 'access-protocol'), 'ro_rule': ('ro-rule', 'security-flavor'), 'rw_rule': ('rw-rule', 'security-flavor'), 'super_user_security': ('super-user-security', 'security-flavor'), } self.na_helper.zapi_bool_keys = { 'allow_suid': 'is-allow-set-uid-enabled' } self.na_helper.zapi_int_keys = { 'rule_index': 'rule-index' } def set_query_parameters(self): """ Return dictionary of query parameters and :return: """ query = { 'policy-name': self.parameters['name'], 'vserver': self.parameters['vserver'] } if self.parameters.get('rule_index'): query['rule-index'] = self.parameters['rule_index'] elif self.parameters.get('client_match'): query['client-match'] = self.parameters['client_match'] else: self.module.fail_json( msg="Need to specify at least one of the rule_index and client_match option.") attributes = { 'query': { 'export-rule-info': query } } return attributes def get_export_policy_rule(self): """ Return details about the export policy rule :param: name : Name of the export_policy :return: Details about the export_policy. None if not found. :rtype: dict """ current, result = None, None rule_iter = netapp_utils.zapi.NaElement('export-rule-get-iter') rule_iter.translate_struct(self.set_query_parameters()) try: result = self.server.invoke_successfully(rule_iter, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error getting export policy rule %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) if result is not None and \ result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) >= 1: current = dict() rule_info = result.get_child_by_name('attributes-list').get_child_by_name('export-rule-info') for item_key, zapi_key in self.na_helper.zapi_string_keys.items(): current[item_key] = rule_info.get_child_content(zapi_key) for item_key, zapi_key in self.na_helper.zapi_bool_keys.items(): current[item_key] = self.na_helper.get_value_for_bool(from_zapi=True, value=rule_info[zapi_key]) for item_key, zapi_key in self.na_helper.zapi_int_keys.items(): current[item_key] = self.na_helper.get_value_for_int(from_zapi=True, value=rule_info[zapi_key]) for item_key, zapi_key in self.na_helper.zapi_list_keys.items(): parent, dummy = zapi_key current[item_key] = self.na_helper.get_value_for_list(from_zapi=True, zapi_parent=rule_info.get_child_by_name(parent)) current['num_records'] = int(result.get_child_content('num-records')) if not self.parameters.get('rule_index'): self.parameters['rule_index'] = current['rule_index'] return current def get_export_policy(self): """ Return details about the export-policy :param: name : Name of the export-policy :return: Details about the export-policy. None if not found. :rtype: dict """ export_policy_iter = netapp_utils.zapi.NaElement('export-policy-get-iter') attributes = { 'query': { 'export-policy-info': { 'policy-name': self.parameters['name'], 'vserver': self.parameters['vserver'] } } } export_policy_iter.translate_struct(attributes) try: result = self.server.invoke_successfully(export_policy_iter, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error getting export policy %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) if result.get_child_by_name('num-records') and int(result.get_child_content('num-records')) == 1: return result return None def add_parameters_for_create_or_modify(self, na_element_object, values): """ Add children node for create or modify NaElement object :param na_element_object: modify or create NaElement object :param values: dictionary of cron values to be added :return: None """ for key in values: if key in self.na_helper.zapi_string_keys: zapi_key = self.na_helper.zapi_string_keys.get(key) na_element_object[zapi_key] = values[key] elif key in self.na_helper.zapi_list_keys: parent_key, child_key = self.na_helper.zapi_list_keys.get(key) na_element_object.add_child_elem(self.na_helper.get_value_for_list(from_zapi=False, zapi_parent=parent_key, zapi_child=child_key, data=values[key])) elif key in self.na_helper.zapi_int_keys: zapi_key = self.na_helper.zapi_int_keys.get(key) na_element_object[zapi_key] = self.na_helper.get_value_for_int(from_zapi=False, value=values[key]) elif key in self.na_helper.zapi_bool_keys: zapi_key = self.na_helper.zapi_bool_keys.get(key) na_element_object[zapi_key] = self.na_helper.get_value_for_bool(from_zapi=False, value=values[key]) def create_export_policy_rule(self): """ create rule for the export policy. """ for key in ['client_match', 'ro_rule', 'rw_rule']: if self.parameters.get(key) is None: self.module.fail_json(msg='Error: Missing required param for creating export policy rule %s' % key) export_rule_create = netapp_utils.zapi.NaElement('export-rule-create') self.add_parameters_for_create_or_modify(export_rule_create, self.parameters) try: self.server.invoke_successfully(export_rule_create, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error creating export policy rule %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def create_export_policy(self): """ Creates an export policy """ export_policy_create = netapp_utils.zapi.NaElement.create_node_with_children( 'export-policy-create', **{'policy-name': self.parameters['name']}) try: self.server.invoke_successfully(export_policy_create, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error creating export-policy %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def delete_export_policy_rule(self, rule_index): """ delete rule for the export policy. """ export_rule_delete = netapp_utils.zapi.NaElement.create_node_with_children( 'export-rule-destroy', **{'policy-name': self.parameters['name'], 'rule-index': str(rule_index)}) try: self.server.invoke_successfully(export_rule_delete, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error deleting export policy rule %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def modify_export_policy_rule(self, params): ''' Modify an existing export policy rule :param params: dict() of attributes with desired values :return: None ''' export_rule_modify = netapp_utils.zapi.NaElement.create_node_with_children( 'export-rule-modify', **{'policy-name': self.parameters['name'], 'rule-index': str(self.parameters['rule_index'])}) self.add_parameters_for_create_or_modify(export_rule_modify, params) try: self.server.invoke_successfully(export_rule_modify, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error modifying allow_suid %s: %s' % (self.parameters['allow_suid'], to_native(error)), exception=traceback.format_exc()) def autosupport_log(self): netapp_utils.ems_log_event("na_ontap_export_policy_rules", self.server) def apply(self): ''' Apply required action from the play''' self.autosupport_log() # convert client_match list to comma-separated string if self.parameters.get('client_match') is not None: self.parameters['client_match'] = ','.join(self.parameters['client_match']) self.parameters['client_match'] = self.parameters['client_match'].replace(' ', '') current, modify = self.get_export_policy_rule(), None action = self.na_helper.get_cd_action(current, self.parameters) if action is None and self.parameters['state'] == 'present': modify = self.na_helper.get_modified_attributes(current, self.parameters) if self.na_helper.changed: if self.module.check_mode: pass else: # create export policy (if policy doesn't exist) only when changed=True if not self.get_export_policy(): self.create_export_policy() if action == 'create': self.create_export_policy_rule() elif action == 'delete': if current['num_records'] > 1: self.module.fail_json(msg='Multiple export policy rules exist.' 'Please specify a rule_index to delete') self.delete_export_policy_rule(current['rule_index']) elif modify: self.modify_export_policy_rule(modify) self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapSecurityKeyManager(object): '''class with key manager operations''' def __init__(self): '''Initialize module parameters''' self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update(state=dict(required=False, choices=['present', 'absent'], default='present'), ip_address=dict(required=True, type='str'), node=dict(required=False, type='str'), tcp_port=dict(required=False, type='int', default=5696)) self.module = AnsibleModule(argument_spec=self.argument_spec, supports_check_mode=True) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.cluster = netapp_utils.setup_na_ontap_zapi(module=self.module) def get_key_manager(self): """ get key manager by ip address. :return: a dict of key manager """ key_manager_info = netapp_utils.zapi.NaElement( 'security-key-manager-get-iter') query_details = netapp_utils.zapi.NaElement.create_node_with_children( 'key-manager-info', **{'key-manager-ip-address': self.parameters['ip_address']}) query = netapp_utils.zapi.NaElement('query') query.add_child_elem(query_details) key_manager_info.add_child_elem(query) try: result = self.cluster.invoke_successfully(key_manager_info, enable_tunneling=False) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error fetching key manager %s : %s' % (self.parameters['node'], to_native(error)), exception=traceback.format_exc()) return_value = None if result.get_child_by_name('num-records') and int( result.get_child_content('num-records')) > 0: key_manager = result.get_child_by_name( 'attributes-list').get_child_by_name('key-manager-info') return_value = {} if key_manager.get_child_by_name('key-manager-ip-address'): return_value['ip_address'] = key_manager.get_child_content( 'key-manager-ip-address') if key_manager.get_child_by_name('key-manager-server-status'): return_value['server_status'] = key_manager.get_child_content( 'key-manager-server-status') if key_manager.get_child_by_name('key-manager-tcp-port'): return_value['tcp_port'] = key_manager.get_child_content( 'key-manager-tcp-port') if key_manager.get_child_by_name('node-name'): return_value['node'] = key_manager.get_child_content( 'node-name') return return_value def key_manager_setup(self): """ set up external key manager. """ key_manager_setup = netapp_utils.zapi.NaElement( 'security-key-manager-setup') # if specify on-boarding passphrase, it is on-boarding key management. # it not, then it's external key management. try: self.cluster.invoke_successfully(key_manager_setup, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error setting up key manager %s : %s' % (self.parameters['node'], to_native(error)), exception=traceback.format_exc()) def create_key_manager(self): """ add key manager. """ key_manager_create = netapp_utils.zapi.NaElement( 'security-key-manager-add') key_manager_create.add_new_child('key-manager-ip-address', self.parameters['ip_address']) if self.parameters.get('tcp_port'): key_manager_create.add_new_child('key-manager-tcp-port', str(self.parameters['tcp_port'])) try: self.cluster.invoke_successfully(key_manager_create, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error creating key manager %s : %s' % (self.parameters['node'], to_native(error)), exception=traceback.format_exc()) def delete_key_manager(self): """ delete key manager. """ key_manager_delete = netapp_utils.zapi.NaElement( 'security-key-manager-delete') key_manager_delete.add_new_child('key-manager-ip-address', self.parameters['ip_address']) try: self.cluster.invoke_successfully(key_manager_delete, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error deleting key manager %s : %s' % (self.parameters['node'], to_native(error)), exception=traceback.format_exc()) def apply(self): self.asup_log_for_cserver("na_ontap_security_key_manager") self.key_manager_setup() current = self.get_key_manager() cd_action = None cd_action = self.na_helper.get_cd_action(current, self.parameters) if self.na_helper.changed: if self.module.check_mode: pass else: if cd_action == 'create': self.create_key_manager() elif cd_action == 'delete': self.delete_key_manager() self.module.exit_json(changed=self.na_helper.changed) def asup_log_for_cserver(self, event_name): """ Fetch admin vserver for the given cluster Create and Autosupport log event with the given module name :param event_name: Name of the event log :return: None """ results = netapp_utils.get_cserver(self.cluster) cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results) netapp_utils.ems_log_event(event_name, cserver)
class NetAppONTAPMotd(object): def __init__(self): argument_spec = netapp_utils.na_ontap_host_argument_spec() argument_spec.update( dict(state=dict(required=False, default='present', choices=['present', 'absent']), vserver=dict(required=True, type='str'), message=dict(default='', type='str'), show_cluster_motd=dict(default=True, type='bool'))) self.module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") self.server = netapp_utils.setup_na_ontap_zapi( module=self.module, vserver=self.parameters['vserver']) def motd_get_iter(self): """ Compose NaElement object to query current motd :return: NaElement object for vserver-motd-get-iter """ motd_get_iter = netapp_utils.zapi.NaElement('vserver-motd-get-iter') query = netapp_utils.zapi.NaElement('query') motd_info = netapp_utils.zapi.NaElement('vserver-motd-info') motd_info.add_new_child('is-cluster-message-enabled', str(self.parameters['show_cluster_motd'])) motd_info.add_new_child('vserver', self.parameters['vserver']) query.add_child_elem(motd_info) motd_get_iter.add_child_elem(query) return motd_get_iter def motd_get(self): """ Get current motd :return: Dictionary of current motd details if query successful, else None """ motd_get_iter = self.motd_get_iter() motd_result = dict() try: result = self.server.invoke_successfully(motd_get_iter, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error fetching motd info: %s' % to_native(error), exception=traceback.format_exc()) if result.get_child_by_name('num-records') and \ int(result.get_child_content('num-records')) > 0: motd_info = result.get_child_by_name( 'attributes-list').get_child_by_name('vserver-motd-info') motd_result['message'] = motd_info.get_child_content('message') motd_result['message'] = str(motd_result['message']).rstrip() motd_result[ 'show_cluster_motd'] = True if motd_info.get_child_content( 'is-cluster-message-enabled') == 'true' else False motd_result['vserver'] = motd_info.get_child_content('vserver') return motd_result return None def modify_motd(self): motd_create = netapp_utils.zapi.NaElement('vserver-motd-modify-iter') motd_create.add_new_child('message', self.parameters['message']) motd_create.add_new_child( 'is-cluster-message-enabled', 'true' if self.parameters['show_cluster_motd'] is True else 'false') query = netapp_utils.zapi.NaElement('query') motd_info = netapp_utils.zapi.NaElement('vserver-motd-info') motd_info.add_new_child('vserver', self.parameters['vserver']) query.add_child_elem(motd_info) motd_create.add_child_elem(query) try: self.server.invoke_successfully(motd_create, enable_tunneling=False) except netapp_utils.zapi.NaApiError as err: self.module.fail_json(msg="Error creating motd: %s" % (to_native(err)), exception=traceback.format_exc()) return motd_create def apply(self): """ Applies action from playbook """ netapp_utils.ems_log_event("na_ontap_motd", self.server) current = self.motd_get() if self.parameters['state'] == 'present' and self.parameters[ 'message'] == "": self.module.fail_json(msg="message parameter cannot be empty") if self.parameters['state'] == 'absent': # Just make sure it is empty self.parameters['message'] = '' if current['message'] == 'None': current = None cd_action = self.na_helper.get_cd_action(current, self.parameters) if cd_action is None and self.parameters['state'] == 'present': self.na_helper.get_modified_attributes(current, self.parameters) if self.na_helper.changed: if self.module.check_mode: pass else: self.modify_motd() self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapDns(object): """ Enable and Disable dns """ def __init__(self): self.use_rest = False self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update( dict(state=dict(required=False, choices=['present', 'absent'], default='present'), vserver=dict(required=True, type='str'), domains=dict(required=False, type='list'), nameservers=dict(required=False, type='list'), skip_validation=dict(required=False, type='bool'))) self.module = AnsibleModule(argument_spec=self.argument_spec, required_if=[('state', 'present', ['domains', 'nameservers'])], supports_check_mode=True) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) # REST API should be used for ONTAP 9.6 or higher, ZAPI for lower version self.restApi = OntapRestAPI(self.module) # some attributes are not supported in earlier REST implementation unsupported_rest_properties = ['skip_validation'] used_unsupported_rest_properties = [ x for x in unsupported_rest_properties if x in self.parameters ] self.use_rest, error = self.restApi.is_rest( used_unsupported_rest_properties) if error is not None: self.module.fail_json(msg=error) if not self.use_rest: if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi( module=self.module, vserver=self.parameters['vserver']) return def create_dns(self): """ Create DNS server :return: none """ if self.use_rest: api = 'name-services/dns' params = {} params['domains'] = self.parameters['domains'] params['servers'] = self.parameters['nameservers'] params['svm'] = {'name': self.parameters['vserver']} message, error = self.restApi.post(api, params) if error: self.module.fail_json(msg=error) else: dns = netapp_utils.zapi.NaElement('net-dns-create') nameservers = netapp_utils.zapi.NaElement('name-servers') domains = netapp_utils.zapi.NaElement('domains') for each in self.parameters['nameservers']: ip_address = netapp_utils.zapi.NaElement('ip-address') ip_address.set_content(each) nameservers.add_child_elem(ip_address) dns.add_child_elem(nameservers) for each in self.parameters['domains']: domain = netapp_utils.zapi.NaElement('string') domain.set_content(each) domains.add_child_elem(domain) dns.add_child_elem(domains) if self.parameters.get('skip_validation'): validation = netapp_utils.zapi.NaElement( 'skip-config-validation') validation.set_content(str(self.parameters['skip_validation'])) dns.add_child_elem(validation) try: self.server.invoke_successfully(dns, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error creating dns: %s' % (to_native(error)), exception=traceback.format_exc()) def destroy_dns(self, dns_attrs): """ Destroys an already created dns :return: """ if self.use_rest: uuid = dns_attrs['records'][0]['svm']['uuid'] api = 'name-services/dns/' + uuid data = None message, error = self.restApi.delete(api, data) if error: self.module.fail_json(msg=error) else: try: self.server.invoke_successfully( netapp_utils.zapi.NaElement('net-dns-destroy'), True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error destroying dns %s' % (to_native(error)), exception=traceback.format_exc()) def get_dns(self): if self.use_rest: api = "name-services/dns" params = { 'fields': 'domains,servers,svm', "svm.name": self.parameters['vserver'] } message, error = self.restApi.get(api, params) if error: self.module.fail_json(msg=error) if len(message.keys()) == 0: message = None elif 'records' in message and len(message['records']) == 0: message = None elif 'records' not in message or len(message['records']) != 1: error = "Unexpected response from %s: %s" % (api, repr(message)) self.module.fail_json(msg=error) return message else: dns_obj = netapp_utils.zapi.NaElement('net-dns-get') try: result = self.server.invoke_successfully(dns_obj, True) except netapp_utils.zapi.NaApiError as error: if to_native(error.code) == "15661": # 15661 is object not found return None else: self.module.fail_json(msg=to_native(error), exception=traceback.format_exc()) # read data for modify attrs = dict() attributes = result.get_child_by_name('attributes') dns_info = attributes.get_child_by_name('net-dns-info') nameservers = dns_info.get_child_by_name('name-servers') attrs['nameservers'] = [ each.get_content() for each in nameservers.get_children() ] domains = dns_info.get_child_by_name('domains') attrs['domains'] = [ each.get_content() for each in domains.get_children() ] attrs['skip_validation'] = dns_info.get_child_by_name( 'skip-config-validation') return attrs def modify_dns(self, dns_attrs): if self.use_rest: changed = False params = {} if dns_attrs['records'][0]['servers'] != self.parameters[ 'nameservers']: changed = True params['servers'] = self.parameters['nameservers'] if dns_attrs['records'][0]['domains'] != self.parameters['domains']: changed = True params['domains'] = self.parameters['domains'] if changed: uuid = dns_attrs['records'][0]['svm']['uuid'] api = "name-services/dns/" + uuid message, error = self.restApi.patch(api, params) if error: self.module.fail_json(msg=error) else: changed = False dns = netapp_utils.zapi.NaElement('net-dns-modify') if dns_attrs['nameservers'] != self.parameters['nameservers']: changed = True nameservers = netapp_utils.zapi.NaElement('name-servers') for each in self.parameters['nameservers']: ip_address = netapp_utils.zapi.NaElement('ip-address') ip_address.set_content(each) nameservers.add_child_elem(ip_address) dns.add_child_elem(nameservers) if dns_attrs['domains'] != self.parameters['domains']: changed = True domains = netapp_utils.zapi.NaElement('domains') for each in self.parameters['domains']: domain = netapp_utils.zapi.NaElement('string') domain.set_content(each) domains.add_child_elem(domain) dns.add_child_elem(domains) if changed: if self.parameters.get('skip_validation'): validation = netapp_utils.zapi.NaElement( 'skip-config-validation') validation.set_content( str(self.parameters['skip_validation'])) dns.add_child_elem(validation) try: self.server.invoke_successfully(dns, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error modifying dns %s' % (to_native(error)), exception=traceback.format_exc()) return changed def apply(self): # asup logging if not self.use_rest: netapp_utils.ems_log_event("na_ontap_dns", self.server) dns_attrs = self.get_dns() changed = False if self.parameters['state'] == 'present': if dns_attrs is not None: changed = self.modify_dns(dns_attrs) else: self.create_dns() changed = True else: if dns_attrs is not None: self.destroy_dns(dns_attrs) changed = True self.module.exit_json(changed=changed)
class NetAppOntapSnapshotPolicy(object): """ Creates and deletes a Snapshot Policy """ def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update( dict( state=dict(required=False, choices=['present', 'absent'], default='present'), name=dict(required=True, type="str"), enabled=dict(required=False, type="bool"), # count is a list of integers count=dict(required=False, type="list", elements="int"), comment=dict(required=False, type="str"), schedule=dict(required=False, type="list", elements="str"), snapmirror_label=dict(required=False, type="list", elements="str"), vserver=dict(required=False, type="str"))) self.module = AnsibleModule(argument_spec=self.argument_spec, required_if=[ ('state', 'present', ['enabled', 'count', 'schedule']), ], supports_check_mode=True) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: if 'vserver' in self.parameters: self.server = netapp_utils.setup_na_ontap_zapi( module=self.module, vserver=self.parameters['vserver']) else: self.server = netapp_utils.setup_na_ontap_zapi( module=self.module) return def get_snapshot_policy(self): """ Checks to see if a snapshot policy exists or not :return: Return policy details if a snapshot policy exists, None if it doesn't """ snapshot_obj = netapp_utils.zapi.NaElement("snapshot-policy-get-iter") # compose query query = netapp_utils.zapi.NaElement("query") snapshot_info_obj = netapp_utils.zapi.NaElement("snapshot-policy-info") snapshot_info_obj.add_new_child("policy", self.parameters['name']) if 'vserver' in self.parameters: snapshot_info_obj.add_new_child("vserver-name", self.parameters['vserver']) query.add_child_elem(snapshot_info_obj) snapshot_obj.add_child_elem(query) try: result = self.server.invoke_successfully(snapshot_obj, True) if result.get_child_by_name('num-records') and \ int(result.get_child_content('num-records')) == 1: snapshot_policy = result.get_child_by_name( 'attributes-list').get_child_by_name( 'snapshot-policy-info') current = {} current['name'] = snapshot_policy.get_child_content('policy') current['vserver'] = snapshot_policy.get_child_content( 'vserver-name') current[ 'enabled'] = False if snapshot_policy.get_child_content( 'enabled').lower() == 'false' else True current['comment'] = snapshot_policy.get_child_content( 'comment') or '' current['schedule'], current['count'], current[ 'snapmirror_label'] = [], [], [] if snapshot_policy.get_child_by_name( 'snapshot-policy-schedules'): for schedule in snapshot_policy[ 'snapshot-policy-schedules'].get_children(): current['schedule'].append( schedule.get_child_content('schedule')) current['count'].append( int(schedule.get_child_content('count'))) snapmirror_label = schedule.get_child_content( 'snapmirror-label') if snapmirror_label is None or snapmirror_label == '-': snapmirror_label = '' current['snapmirror_label'].append(snapmirror_label) return current except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg=to_native(error), exception=traceback.format_exc()) return None def validate_parameters(self): """ Validate if each schedule has a count associated :return: None """ if 'count' not in self.parameters or 'schedule' not in self.parameters or \ len(self.parameters['count']) > 5 or len(self.parameters['schedule']) > 5 or \ len(self.parameters['count']) < 1 or len(self.parameters['schedule']) < 1 or \ len(self.parameters['count']) != len(self.parameters['schedule']): self.module.fail_json( msg="Error: A Snapshot policy must have at least 1 " "schedule and can have up to a maximum of 5 schedules, with a count " "representing the maximum number of Snapshot copies for each schedule" ) if 'snapmirror_label' in self.parameters: if len(self.parameters['snapmirror_label']) != len( self.parameters['schedule']): self.module.fail_json( msg="Error: Each Snapshot Policy schedule must have an " "accompanying SnapMirror Label") def modify_snapshot_policy(self, current): """ Modifies an existing snapshot policy """ # Set up required variables to modify snapshot policy options = {'policy': self.parameters['name']} modify = False # Set up optional variables to modify snapshot policy if 'enabled' in self.parameters and self.parameters[ 'enabled'] != current['enabled']: options['enabled'] = str(self.parameters['enabled']) modify = True if 'comment' in self.parameters and self.parameters[ 'comment'] != current['comment']: options['comment'] = self.parameters['comment'] modify = True if modify: snapshot_obj = netapp_utils.zapi.NaElement.create_node_with_children( 'snapshot-policy-modify', **options) try: self.server.invoke_successfully(snapshot_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg='Error modifying snapshot policy %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def modify_snapshot_policy_schedules(self, current): """ Modify existing schedules in snapshot policy :return: None """ self.validate_parameters() delete_schedules, modify_schedules, add_schedules = [], [], [] if 'snapmirror_label' in self.parameters: snapmirror_labels = self.parameters['snapmirror_label'] else: # User hasn't supplied any snapmirror labels. snapmirror_labels = [None] * len(self.parameters['schedule']) # Identify schedules for deletion for schedule in current['schedule']: schedule = schedule.strip() if schedule not in [ item.strip() for item in self.parameters['schedule'] ]: options = {'policy': current['name'], 'schedule': schedule} delete_schedules.append(options) # Identify schedules to be modified or added for schedule, count, snapmirror_label in zip( self.parameters['schedule'], self.parameters['count'], snapmirror_labels): schedule = schedule.strip() if snapmirror_label is not None: snapmirror_label = snapmirror_label.strip() options = {'policy': current['name'], 'schedule': schedule} if schedule in current['schedule']: # Schedule exists. Only modify if it has changed. modify = False schedule_index = current['schedule'].index(schedule) if count != current['count'][schedule_index]: options['new-count'] = str(count) modify = True if snapmirror_label is not None: if snapmirror_label != current['snapmirror_label'][ schedule_index]: options['new-snapmirror-label'] = snapmirror_label modify = True if modify: modify_schedules.append(options) else: # New schedule options['count'] = str(count) if snapmirror_label is not None and snapmirror_label != '': options['snapmirror-label'] = snapmirror_label add_schedules.append(options) # Delete N-1 schedules no longer required. Must leave 1 schedule in policy # at any one time. Delete last one afterwards. while len(delete_schedules) > 1: options = delete_schedules.pop() self.modify_snapshot_policy_schedule( options, 'snapshot-policy-remove-schedule') # Modify schedules. while len(modify_schedules) > 0: options = modify_schedules.pop() self.modify_snapshot_policy_schedule( options, 'snapshot-policy-modify-schedule') # Add N-1 new schedules. Add last one after last schedule has been deleted. while len(add_schedules) > 1: options = add_schedules.pop() self.modify_snapshot_policy_schedule( options, 'snapshot-policy-add-schedule') # Delete last schedule no longer required. while len(delete_schedules) > 0: options = delete_schedules.pop() self.modify_snapshot_policy_schedule( options, 'snapshot-policy-remove-schedule') # Add last new schedule. while len(add_schedules) > 0: options = add_schedules.pop() self.modify_snapshot_policy_schedule( options, 'snapshot-policy-add-schedule') def modify_snapshot_policy_schedule(self, options, zapi): """ Add, modify or remove a schedule to/from a snapshot policy """ snapshot_obj = netapp_utils.zapi.NaElement.create_node_with_children( zapi, **options) try: self.server.invoke_successfully(snapshot_obj, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg='Error modifying snapshot policy schedule %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def create_snapshot_policy(self): """ Creates a new snapshot policy """ # set up required variables to create a snapshot policy self.validate_parameters() options = { 'policy': self.parameters['name'], 'enabled': str(self.parameters['enabled']), } if 'snapmirror_label' in self.parameters: snapmirror_labels = self.parameters['snapmirror_label'] else: # User hasn't supplied any snapmirror labels. snapmirror_labels = [None] * len(self.parameters['schedule']) # zapi attribute for first schedule is schedule1, second is schedule2 and so on positions = [ str(i) for i in range(1, len(self.parameters['schedule']) + 1) ] for schedule, count, snapmirror_label, position in zip( self.parameters['schedule'], self.parameters['count'], snapmirror_labels, positions): schedule = schedule.strip() options['count' + position] = str(count) options['schedule' + position] = schedule if snapmirror_label is not None: snapmirror_label = snapmirror_label.strip() if snapmirror_label != '': options['snapmirror-label' + position] = snapmirror_label snapshot_obj = netapp_utils.zapi.NaElement.create_node_with_children( 'snapshot-policy-create', **options) # Set up optional variables to create a snapshot policy if self.parameters.get('comment'): snapshot_obj.add_new_child("comment", self.parameters['comment']) try: self.server.invoke_successfully(snapshot_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error creating snapshot policy %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def delete_snapshot_policy(self): """ Deletes an existing snapshot policy """ snapshot_obj = netapp_utils.zapi.NaElement("snapshot-policy-delete") # Set up required variables to delete a snapshot policy snapshot_obj.add_new_child("policy", self.parameters['name']) try: self.server.invoke_successfully(snapshot_obj, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error deleting snapshot policy %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) def asup_log_for_cserver(self, event_name): """ Fetch admin vserver for the given cluster Create and Autosupport log event with the given module name :param event_name: Name of the event log :return: None """ results = netapp_utils.get_cserver(self.server) cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results) netapp_utils.ems_log_event(event_name, cserver) def apply(self): """ Check to see which play we should run """ self.asup_log_for_cserver("na_ontap_snapshot_policy") current = self.get_snapshot_policy() modify = None cd_action = self.na_helper.get_cd_action(current, self.parameters) if cd_action is None and self.parameters['state'] == 'present': # Don't sort schedule/count/snapmirror_label lists as it can # mess up the intended parameter order. modify = self.na_helper.get_modified_attributes( current, self.parameters) if self.na_helper.changed: if self.module.check_mode: pass else: if cd_action == 'create': self.create_snapshot_policy() elif cd_action == 'delete': self.delete_snapshot_policy() if modify: self.modify_snapshot_policy(current) self.modify_snapshot_policy_schedules(current) self.module.exit_json(changed=self.na_helper.changed)
class NetAppOntapadapter(object): ''' object to describe adapter 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'], default='present'), adapter_name=dict(required=True, type='str'), node_name=dict(required=True, type='str'), mode=dict(required=False, type='str'), type=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) def get_adapter(self): """ Return details about the adapter :param: name : Name of the name of the adapter :return: Details about the adapter. None if not found. :rtype: dict """ adapter_info = netapp_utils.zapi.NaElement('ucm-adapter-get') adapter_info.add_new_child('adapter-name', self.parameters['adapter_name']) adapter_info.add_new_child('node-name', self.parameters['node_name']) try: result = self.server.invoke_successfully(adapter_info, True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json(msg='Error fetching ucadapter details: %s: %s' % (self.parameters['node_name'], to_native(error)), exception=traceback.format_exc()) if result.get_child_by_name('attributes'): adapter_attributes = result.get_child_by_name('attributes').\ get_child_by_name('uc-adapter-info') return_value = { 'mode': adapter_attributes.get_child_content('mode'), 'pending-mode': adapter_attributes.get_child_content('pending-mode'), 'type': adapter_attributes.get_child_content('fc4-type'), 'pending-type': adapter_attributes.get_child_content('pending-fc4-type'), 'status': adapter_attributes.get_child_content('status'), } return return_value return None def modify_adapter(self): """ Modify the adapter. """ params = {'adapter-name': self.parameters['adapter_name'], 'node-name': self.parameters['node_name']} if self.parameters['type'] is not None: params['fc4-type'] = self.parameters['type'] if self.parameters['mode'] is not None: params['mode'] = self.parameters['mode'] adapter_modify = netapp_utils.zapi.NaElement.create_node_with_children( 'ucm-adapter-modify', ** params) try: self.server.invoke_successfully(adapter_modify, enable_tunneling=True) except netapp_utils.zapi.NaApiError as e: self.module.fail_json(msg='Error modifying adapter %s: %s' % (self.parameters['adapter_name'], to_native(e)), exception=traceback.format_exc()) def online_or_offline_adapter(self, status): """ Bring a Fibre Channel target adapter offline/online. """ if status == 'down': adapter = netapp_utils.zapi.NaElement('fcp-adapter-config-down') elif status == 'up': adapter = netapp_utils.zapi.NaElement('fcp-adapter-config-up') adapter.add_new_child('fcp-adapter', self.parameters['adapter_name']) adapter.add_new_child('node', self.parameters['node_name']) try: self.server.invoke_successfully(adapter, enable_tunneling=True) except netapp_utils.zapi.NaApiError as e: self.module.fail_json(msg='Error trying to %s fc-adapter %s: %s' % (status, self.parameters['adapter_name'], to_native(e)), exception=traceback.format_exc()) def autosupport_log(self): """ Autosupport log for ucadater :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_ucadapter", cserver) def apply(self): ''' calling all adapter features ''' changed = False adapter_detail = self.get_adapter() def need_to_change(expected, pending, current): if expected is None: return False elif pending is not None: return pending != expected elif current is not None: return current != expected return False if adapter_detail: changed = need_to_change(self.parameters.get('type'), adapter_detail['pending-type'], adapter_detail['type']) or need_to_change(self.parameters.get('mode'), adapter_detail['pending-mode'], adapter_detail['mode']) if changed: if self.module.check_mode: pass else: self.online_or_offline_adapter('down') self.modify_adapter() self.online_or_offline_adapter('up') self.module.exit_json(changed=changed)
class NetAppONTAPVserverPeer(object): """ Class with vserver peer methods """ def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update( dict(state=dict(required=False, type='str', choices=['present', 'absent'], default='present'), vserver=dict(required=True, type='str'), peer_vserver=dict(required=True, type='str'), peer_cluster=dict(required=False, type='str'), applications=dict(required=False, type='list', choices=[ 'snapmirror', 'file_copy', 'lun_copy', 'flexcache' ]), dest_hostname=dict(required=False, type='str'), dest_username=dict(required=False, type='str'), dest_password=dict(required=False, type='str', no_log=True))) self.module = AnsibleModule(argument_spec=self.argument_spec, supports_check_mode=True) self.na_helper = NetAppModule() self.parameters = self.na_helper.set_parameters(self.module.params) if HAS_NETAPP_LIB is False: self.module.fail_json( msg="the python NetApp-Lib module is required") else: self.server = netapp_utils.setup_na_ontap_zapi(module=self.module) if self.parameters.get('dest_hostname'): self.module.params['hostname'] = self.parameters[ 'dest_hostname'] if self.parameters.get('dest_username'): self.module.params['username'] = self.parameters[ 'dest_username'] if self.parameters.get('dest_password'): self.module.params['password'] = self.parameters[ 'dest_password'] self.dest_server = netapp_utils.setup_na_ontap_zapi( module=self.module) # reset to source host connection for asup logs self.module.params['hostname'] = self.parameters['hostname'] def vserver_peer_get_iter(self): """ Compose NaElement object to query current vserver using peer-vserver and vserver parameters :return: NaElement object for vserver-get-iter with query """ vserver_peer_get = netapp_utils.zapi.NaElement('vserver-peer-get-iter') query = netapp_utils.zapi.NaElement('query') vserver_peer_info = netapp_utils.zapi.NaElement('vserver-peer-info') vserver_peer_info.add_new_child('peer-vserver', self.parameters['peer_vserver']) vserver_peer_info.add_new_child('vserver', self.parameters['vserver']) query.add_child_elem(vserver_peer_info) vserver_peer_get.add_child_elem(query) return vserver_peer_get def vserver_peer_get(self): """ Get current vserver peer info :return: Dictionary of current vserver peer details if query successful, else return None """ vserver_peer_get_iter = self.vserver_peer_get_iter() vserver_info = dict() try: result = self.server.invoke_successfully(vserver_peer_get_iter, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg='Error fetching vserver peer %s: %s' % (self.parameters['vserver'], to_native(error)), exception=traceback.format_exc()) # return vserver peer details if result.get_child_by_name('num-records') and \ int(result.get_child_content('num-records')) > 0: vserver_peer_info = result.get_child_by_name( 'attributes-list').get_child_by_name('vserver-peer-info') vserver_info['peer_vserver'] = vserver_peer_info.get_child_content( 'peer-vserver') vserver_info['vserver'] = vserver_peer_info.get_child_content( 'vserver') vserver_info['peer_state'] = vserver_peer_info.get_child_content( 'peer-state') return vserver_info return None def vserver_peer_delete(self): """ Delete a vserver peer """ vserver_peer_delete = netapp_utils.zapi.NaElement.create_node_with_children( 'vserver-peer-delete', **{ 'peer-vserver': self.parameters['peer_vserver'], 'vserver': self.parameters['vserver'] }) try: self.server.invoke_successfully(vserver_peer_delete, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg='Error deleting vserver peer %s: %s' % (self.parameters['vserver'], to_native(error)), exception=traceback.format_exc()) def get_peer_cluster_name(self): """ Get local cluster name :return: cluster name """ cluster_info = netapp_utils.zapi.NaElement('cluster-identity-get') try: result = self.server.invoke_successfully(cluster_info, enable_tunneling=True) return result.get_child_by_name('attributes').get_child_by_name( 'cluster-identity-info').get_child_content('cluster-name') except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg='Error fetching peer cluster name for peer vserver %s: %s' % (self.parameters['peer_vserver'], to_native(error)), exception=traceback.format_exc()) def vserver_peer_create(self): """ Create a vserver peer """ if self.parameters.get('applications') is None: self.module.fail_json(msg='applications parameter is missing') if self.parameters.get( 'peer_cluster' ) is not None and self.parameters.get('dest_hostname') is None: self.module.fail_json( msg= 'dest_hostname is required for peering a vserver in remote cluster' ) if self.parameters.get('peer_cluster') is None: self.parameters['peer_cluster'] = self.get_peer_cluster_name() vserver_peer_create = netapp_utils.zapi.NaElement.create_node_with_children( 'vserver-peer-create', **{ 'peer-vserver': self.parameters['peer_vserver'], 'vserver': self.parameters['vserver'], 'peer-cluster': self.parameters['peer_cluster'] }) applications = netapp_utils.zapi.NaElement('applications') for application in self.parameters['applications']: applications.add_new_child('vserver-peer-application', application) vserver_peer_create.add_child_elem(applications) try: self.server.invoke_successfully(vserver_peer_create, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg='Error creating vserver peer %s: %s' % (self.parameters['vserver'], to_native(error)), exception=traceback.format_exc()) def is_remote_peer(self): if self.parameters.get('dest_hostname') is None or \ (self.parameters['dest_hostname'] == self.parameters['hostname']): return False return True def vserver_peer_accept(self): """ Accept a vserver peer at destination """ # peer-vserver -> remote (source vserver is provided) # vserver -> local (destination vserver is provided) vserver_peer_accept = netapp_utils.zapi.NaElement.create_node_with_children( 'vserver-peer-accept', **{ 'peer-vserver': self.parameters['vserver'], 'vserver': self.parameters['peer_vserver'] }) try: self.dest_server.invoke_successfully(vserver_peer_accept, enable_tunneling=True) except netapp_utils.zapi.NaApiError as error: self.module.fail_json( msg='Error accepting vserver peer %s: %s' % (self.parameters['peer_vserver'], to_native(error)), exception=traceback.format_exc()) def apply(self): """ Apply action to create/delete or accept vserver peer """ results = netapp_utils.get_cserver(self.server) cserver = netapp_utils.setup_na_ontap_zapi(module=self.module, vserver=results) netapp_utils.ems_log_event("na_ontap_vserver_peer", cserver) current = self.vserver_peer_get() cd_action = self.na_helper.get_cd_action(current, self.parameters) if cd_action == 'create': self.vserver_peer_create() # accept only if the peer relationship is on a remote cluster if self.is_remote_peer(): self.vserver_peer_accept() elif cd_action == 'delete': self.vserver_peer_delete() self.module.exit_json(changed=self.na_helper.changed)
class 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)