diff --git a/ansible_collections/netapp/azure/netapp-azure-20.8.0.tar.gz b/ansible_collections/netapp/azure/netapp-azure-20.8.0.tar.gz index 9355bf09..769b03db 100644 Binary files a/ansible_collections/netapp/azure/netapp-azure-20.8.0.tar.gz and b/ansible_collections/netapp/azure/netapp-azure-20.8.0.tar.gz differ diff --git a/ansible_collections/netapp/ontap/README.md b/ansible_collections/netapp/ontap/README.md index 04bdeb52..c196ace0 100644 --- a/ansible_collections/netapp/ontap/README.md +++ b/ansible_collections/netapp/ontap/README.md @@ -46,10 +46,12 @@ Join our Slack Channel at [Netapp.io](http://netapp.io/slack) - na_ontap_volume - `inline-compression` to enable inline compression on a volume. - na_ontap_volume - `nas_application_template` to create a volume using nas application REST API. - na_ontap_volume - `size_change_threshold` to ignore small changes in volume size. + - na_ontap_volume - `sizing_method` to resize a FlexGroup using REST. ### Bug fixes - na_ontap_broadcast_domain_ports - properly report check_mode `changed`. - na_ontap_cifs - fix for AttributeError - 'NoneType' object has no attribute 'get' on line 300 + - na_ontap_user - application parameter expects only ``service_processor`` but module supports ``service-processor``. - na_ontap_volume - change in volume type was ignored and now reporting an error. - na_ontap_volume - checking for success before failure lead to 'NoneType' object has no attribute 'get_child_by_name' when modifying a Flexcache volume. diff --git a/ansible_collections/netapp/ontap/changelogs/fragments/DEVOPS-3386.yaml b/ansible_collections/netapp/ontap/changelogs/fragments/DEVOPS-3386.yaml new file mode 100644 index 00000000..999c5c74 --- /dev/null +++ b/ansible_collections/netapp/ontap/changelogs/fragments/DEVOPS-3386.yaml @@ -0,0 +1,2 @@ +bugfixes: + - na_ontap_user - application expects only ``service_processor`` but module supports ``service-processor``. diff --git a/ansible_collections/netapp/ontap/changelogs/fragments/DEVOPS-3454.yaml b/ansible_collections/netapp/ontap/changelogs/fragments/DEVOPS-3454.yaml new file mode 100644 index 00000000..babca44d --- /dev/null +++ b/ansible_collections/netapp/ontap/changelogs/fragments/DEVOPS-3454.yaml @@ -0,0 +1,2 @@ +minor_changes: + - na_ontap_volume - ``sizing_method`` to resize a FlexGroup using REST. diff --git a/ansible_collections/netapp/ontap/plugins/modules/na_ontap_user.py b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_user.py index 2d244a3f..26690f0a 100644 --- a/ansible_collections/netapp/ontap/plugins/modules/na_ontap_user.py +++ b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_user.py @@ -45,11 +45,13 @@ description: - List of application to grant access to. - Creating a login with application console, telnet, rsh, and service-processor for a data Vserver is not supported. + - Module supports both service-processor and service_processor choices. + - ZAPI requires service-processor, while REST requires service_processor, except for an issue with ONTAP 9.6 and 9.7. - snmp is not supported in REST. required: true type: list elements: str - choices: ['console', 'http','ontapi','rsh','snmp','service-processor','sp','ssh','telnet'] + choices: ['console', 'http','ontapi','rsh','snmp','service_processor','service-processor','sp','ssh','telnet'] aliases: - application authentication_method: @@ -219,7 +221,7 @@ def __init__(self): applications=dict(required=True, type='list', elements='str', aliases=['application'], choices=['console', 'http', 'ontapi', 'rsh', 'snmp', - 'sp', 'service-processor', 'ssh', 'telnet'],), + 'sp', 'service-processor', '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), @@ -366,6 +368,26 @@ def create_user_rest(self, apps=None): if 'lock_user' in self.parameters: params['locked'] = self.parameters['lock_user'] dummy, error = self.rest_api.post(api, params) + error_sp = None + if error: + if 'invalid value' in error['message']: + if 'service-processor' in error['message'] or 'service_processor' in error['message']: + # find if there is error for service processor application value + # update value as per ONTAP version support + app_list_sp = params['applications'] + for app_item in app_list_sp: + if 'service-processor' == app_item['application']: + app_item['application'] = 'service_processor' + elif 'service_processor' == app_item['application']: + app_item['application'] = 'service-processor' + params['applications'] = app_list_sp + # post again and throw first error in case of an error + dummy, error_sp = self.rest_api.post(api, params) + if error_sp: + self.module.fail_json(msg='Error while creating user: %s' % error) + return True + + # non-sp errors thrown if error: self.module.fail_json(msg='Error while creating user: %s' % error) @@ -581,12 +603,23 @@ def modify_user(self, application): self.module.fail_json(msg='Error modifying user %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) + def change_sp_application(self, current_app): + if 'service-processor' or 'service_processor' in self.parameters['applications']: + if 'service-processor' in current_app: + if 'service_processor' in self.parameters['applications']: + index = self.parameters['applications'].index('service_processor') + self.parameters['applications'][index] = 'service-processor' + if 'service_processor' in current_app: + if 'service-processor' in self.parameters['applications']: + index = self.parameters['applications'].index('service-processor') + self.parameters['applications'][index] = 'service_processor' + def apply_for_rest(self): current = self.get_user_rest() - if current is not None: uuid, name = current current = self.get_user_details_rest(name, uuid) + self.change_sp_application(current['applications']) cd_action = self.na_helper.get_cd_action(current, self.parameters) modify_decision = self.na_helper.get_modified_attributes(current, self.parameters) diff --git a/ansible_collections/netapp/ontap/plugins/modules/na_ontap_volume.py b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_volume.py index 66b6c7c6..94df84bd 100644 --- a/ansible_collections/netapp/ontap/plugins/modules/na_ontap_volume.py +++ b/ansible_collections/netapp/ontap/plugins/modules/na_ontap_volume.py @@ -176,6 +176,16 @@ default: 10 version_added: 20.12.0 + sizing_method: + description: + - Represents the method to modify the size of a FlexGroup. + - use_existing_resources - Increases or decreases the size of the FlexGroup by increasing or decreasing the size of the current FlexGroup resources. + - add_new_resources - Increases the size of the FlexGroup by adding new resources. This is limited to two new resources per available aggregate. + - This is only supported if REST is enabled (ONTAP 9.6 or later) and only for FlexGroups. ONTAP defaults to use_existing_resources. + type: str + choices: ['add_new_resources', 'use_existing_resources'] + version_added: 20.12.0 + type: description: - The volume type, either read-write (RW) or data-protection (DP). @@ -755,6 +765,7 @@ def __init__(self): is_online=dict(required=False, type='bool', default=True), size=dict(type='int', default=None), size_unit=dict(default='gb', choices=['bytes', 'b', 'kb', 'mb', 'gb', 'tb', 'pb', 'eb', 'zb', 'yb'], type='str'), + sizing_method=dict(choices=['add_new_resources', 'use_existing_resources'], type='str'), aggregate_name=dict(type='str', default=None), type=dict(type='str', default=None), export_policy=dict(type='str', default=None, aliases=['policy']), @@ -988,6 +999,7 @@ def get_volume(self, vol_name=None): return_value['comment'] = volume_id_attributes['comment'] else: return_value['comment'] = None + return_value['uuid'] = self.na_helper.safe_get(volume_id_attributes, ['instance-uuid']) if volume_attributes['volume-security-attributes'].get_child_by_name('style'): # style is not present if the volume is still offline or of type: dp return_value['volume_security_style'] = volume_attributes['volume-security-attributes']['style'] @@ -1072,9 +1084,11 @@ def get_volume(self, vol_name=None): return return_value - def fail_on_error(self, error, stack=False): + def fail_on_error(self, error, api=None, stack=False): if error is None: return + if api is not None: + error = 'calling api: %s: %s' % (api, error) results = dict(msg="Error: %s" % error) if stack: results['stack'] = traceback.format_stack() @@ -1390,13 +1404,31 @@ def rename_volume(self): % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) + def rest_resize_volume(self): + """ + Re-size the volume using REST PATCH method. + """ + uuid = self.parameters['uuid'] + if uuid is None: + self.module.fail_json(msg='Could not read UUID for volume %s' % self.parameters['name']) + api = '/storage/volumes/%s' % uuid + body = dict(size=self.parameters['size']) + query = dict(sizing_method=self.parameters['sizing_method']) + rest_api = netapp_utils.OntapRestAPI(self.module) + response, error = rest_api.patch(api, body, query) + self.fail_on_error(error, api) + return response + def resize_volume(self): """ Re-size the volume. - Note: 'is_infinite' needs to be set to True in order to rename an + Note: 'is_infinite' needs to be set to True in order to resize an Infinite Volume. """ + if self.parameters.get('sizing_method') is not None: + return self.rest_resize_volume() + vol_size_zapi, vol_name_zapi = ['volume-size-async', 'volume-name']\ if (self.parameters['is_infinite'] or self.volume_style == 'flexGroup')\ else ['volume-size', 'volume'] @@ -1412,6 +1444,7 @@ def resize_volume(self): self.module.fail_json(msg='Error re-sizing volume %s: %s' % (self.parameters['name'], to_native(error)), exception=traceback.format_exc()) + return None def change_volume_state(self, call_from_delete_vol=False): """ @@ -2015,6 +2048,7 @@ def apply(self): elif cd_action == 'delete': self.delete_volume(current) elif modify: + self.parameters['uuid'] = current['uuid'] self.take_modify_actions(modify) result = dict( diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume.py index 4ffb4746..48c34856 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume.py @@ -796,7 +796,8 @@ def test_successful_resize_flex_group(self, get_volume): 'vserver': self.mock_vol['vserver'], 'style_extended': 'flexgroup', 'size': 20971520, - 'unix_permissions': '755' + 'unix_permissions': '755', + 'uuid': '1234' } get_volume.side_effect = [ current @@ -821,7 +822,8 @@ def test_successful_modify_unix_permissions_flex_group(self, get_volume, check_j 'name': self.mock_vol['name'], 'vserver': self.mock_vol['vserver'], 'style_extended': 'flexgroup', - 'unix_permissions': '777' + 'unix_permissions': '777', + 'uuid': '1234' } get_volume.side_effect = [ current @@ -849,7 +851,8 @@ def test_successful_modify_unix_permissions_flex_group_0_time_out(self, get_volu 'name': self.mock_vol['name'], 'vserver': self.mock_vol['vserver'], 'style_extended': 'flexgroup', - 'unix_permissions': '777' + 'unix_permissions': '777', + 'uuid': '1234' } get_volume.side_effect = [ current @@ -873,7 +876,8 @@ def test_successful_modify_unix_permissions_flex_group_0_missing_result(self, ge 'name': self.mock_vol['name'], 'vserver': self.mock_vol['vserver'], 'style_extended': 'flexgroup', - 'unix_permissions': '777' + 'unix_permissions': '777', + 'uuid': '1234' } get_volume.side_effect = [ current @@ -899,7 +903,8 @@ def test_error_modify_unix_permissions_flex_group(self, get_volume, check_job_st 'name': self.mock_vol['name'], 'vserver': self.mock_vol['vserver'], 'style_extended': 'flexgroup', - 'unix_permissions': '777' + 'unix_permissions': '777', + 'uuid': '1234' } get_volume.side_effect = [ current @@ -924,7 +929,8 @@ def test_failure_modify_unix_permissions_flex_group(self, get_volume): 'name': self.mock_vol['name'], 'vserver': self.mock_vol['vserver'], 'style_extended': 'flexvol', - 'unix_permissions': '777' + 'unix_permissions': '777', + 'uuid': '1234' } get_volume.side_effect = [ current @@ -949,7 +955,8 @@ def test_successful_offline_state_flex_group(self, get_volume): 'style_extended': 'flexgroup', 'is_online': True, 'junction_path': 'anything', - 'unix_permissions': '755' + 'unix_permissions': '755', + 'uuid': '1234' } get_volume.side_effect = [ current @@ -973,7 +980,8 @@ def test_successful_online_state_flex_group(self, get_volume): 'style_extended': 'flexgroup', 'is_online': False, 'junction_path': 'anything', - 'unix_permissions': '755' + 'unix_permissions': '755', + 'uuid': '1234' } get_volume.side_effect = [ current @@ -1090,7 +1098,8 @@ def test_successful_modify_snapshot_auto_delete(self, get_volume): 'vserver': self.mock_vol['vserver'], 'snapshot_auto_delete': {'delete_order': 'newest_first', 'destroy_list': 'lun_clone,vol_clone', 'target_free_space': 30, 'prefix': 'test', 'commitment': 'try', - 'state': 'on', 'trigger': 'snap_reserve', 'defer_delete': 'scheduled'} + 'state': 'on', 'trigger': 'snap_reserve', 'defer_delete': 'scheduled'}, + 'uuid': '1234' } get_volume.side_effect = [ current diff --git a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_rest.py b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_rest.py index 30238e9b..fe5016e3 100644 --- a/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_rest.py +++ b/ansible_collections/netapp/ontap/tests/unit/plugins/modules/test_na_ontap_volume_rest.py @@ -132,7 +132,8 @@ def build_get_response(self, name): 'attributes-list': [{ 'volume-attributes': { 'volume-id-attributes': { - 'name': name + 'name': name, + 'instance-uuid': '123' }, 'volume-performance-attributes': { 'is-atime-update-enabled': 'true' @@ -309,3 +310,24 @@ def test_rest_successfully_created_with_modify(self, mock_request): assert 'unix_permissions' in exc.value.args[0]['modify_after_create'] assert 'language' not in exc.value.args[0]['modify_after_create'] # eh! assert 'volume-modify-iter' in my_volume.server.zapis + + @patch('ansible_collections.netapp.ontap.plugins.module_utils.netapp.OntapRestAPI.send_request') + def test_rest_successfully_resized(self, mock_request): + ''' make sure resize if using RESP API if sizing_method is present + ''' + data = dict(self.mock_args()) + data['sizing_method'] = 'add_new_resources' + data['size'] = 20737418240 + set_module_args(data) + mock_request.side_effect = [ + SRR['empty_good'], # PATCH application/applications + SRR['end_of_sequence'] + ] + my_volume = self.get_volume_mock_object(get_volume=['test']) + with pytest.raises(AnsibleExitJson) as exc: + my_volume.apply() + assert exc.value.args[0]['changed'] + print(exc.value.args[0]) + assert 'volume-size' not in my_volume.server.zapis + print(mock_request.call_args) + mock_request.assert_called_with('PATCH', '/storage/volumes/123', {'sizing_method': 'add_new_resources'}, json={'size': 22266633286068469760})