1# This code is part of Ansible, but is an independent component. 2# This particular file snippet, and this file snippet only, is BSD licensed. 3# Modules you write using this snippet, which is embedded dynamically by Ansible 4# still belong to the author of the module, and may assign their own license 5# to the complete work. 6# 7# Copyright (c) 2020, Laurent Nicolas <laurentn@netapp.com> 8# All rights reserved. 9# 10# Redistribution and use in source and binary forms, with or without modification, 11# are permitted provided that the following conditions are met: 12# 13# * Redistributions of source code must retain the above copyright 14# notice, this list of conditions and the following disclaimer. 15# * Redistributions in binary form must reproduce the above copyright notice, 16# this list of conditions and the following disclaimer in the documentation 17# and/or other materials provided with the distribution. 18# 19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 24# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 27# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29""" Support functions for NetApp ansible modules 30 31 Provides common processing for responses and errors from REST calls 32""" 33 34from __future__ import (absolute_import, division, print_function) 35__metaclass__ = type 36 37 38def api_error(api, error): 39 """format error message for api error, if error is present""" 40 if error is not None: 41 return "calling: %s: got %s." % (api, error) 42 return None 43 44 45def no_response_error(api, response): 46 """format error message for empty response""" 47 return "calling: %s: no response %s." % (api, repr(response)) 48 49 50def job_error(response, error): 51 """format error message for job error""" 52 return "job reported error: %s, received %s." % (error, repr(response)) 53 54 55def unexpected_response_error(api, response, query=None): 56 """format error message for reponse not matching expectations""" 57 msg = "calling: %s: unexpected response %s." % (api, repr(response)) 58 if query: 59 msg += " for query: %s" % repr(query) 60 return response, msg 61 62 63def get_num_records(response): 64 # num_records is not always present! 65 if 'num_records' in response: 66 return response['num_records'] 67 if 'records' in response: 68 return len(response['records']) 69 return 1 70 71 72def check_for_0_or_1_records(api, response, error, query=None): 73 """return None if no record was returned by the API 74 return record if one record was returned by the API 75 return error otherwise (error, no response, more than 1 record) 76 """ 77 if error: 78 if api: 79 return None, api_error(api, error) 80 return None, error 81 if not response: 82 return None, no_response_error(api, response) 83 num_records = get_num_records(response) 84 if num_records == 0: 85 return None, None # not found 86 if num_records != 1: 87 return unexpected_response_error(api, response, query) 88 if 'records' in response: 89 return response['records'][0], None 90 return response, None 91 92 93def check_for_0_or_more_records(api, response, error): 94 """return None if no record was returned by the API 95 return records if one or more records was returned by the API 96 return error otherwise (error, no response) 97 """ 98 if error: 99 if api: 100 return None, api_error(api, error) 101 return None, error 102 if not response: 103 return None, no_response_error(api, response) 104 if get_num_records(response) == 0: 105 return None, None # not found 106 return response['records'], None 107 108 109def check_for_error_and_job_results(api, response, error, rest_api, **kwargs): 110 """report first error if present 111 otherwise call wait_on_job and retrieve job response or error 112 """ 113 if error: 114 error = api_error(api, error) 115 # we expect two types of response 116 # a plain response, for synchronous calls 117 # or a job response, for asynchronous calls 118 # and it's possible to expect both when 'return_timeout' > 0 119 elif isinstance(response, dict) and 'job' in response: 120 job_response, error = rest_api.wait_on_job(response['job'], **kwargs) 121 if error: 122 error = job_error(response, error) 123 else: 124 response['job_response'] = job_response 125 return response, error 126