Server IP : 85.214.239.14 / Your IP : 52.14.97.44 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /lib/python3/dist-packages/ansible_collections/netapp/ontap/plugins/modules/ |
Upload File : |
#!/usr/bin/python ''' # (c) 2020, NetApp, Inc # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ''' from __future__ import absolute_import, division, print_function __metaclass__ = type ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'certified'} DOCUMENTATION = ''' author: NetApp Ansible Team (@carchi8py) <ng-ansibleteam@netapp.com> description: - Call a REST API on ONTAP. - Cluster REST API are run using a cluster admin account. - Vserver REST API can be run using a vsadmin account or using vserver tunneling (cluster admin with I(vserver_) options). - In case of success, a json dictionary is returned as C(response). - In case of a REST API error, C(status_code), C(error_code), C(error_message) are set to help with diagnosing the issue, - and the call is reported as an error ('failed'). - Other errors (eg connection issues) are reported as Ansible error. extends_documentation_fragment: - netapp.ontap.netapp.na_ontap module: na_ontap_restit short_description: NetApp ONTAP Run any REST API on ONTAP version_added: "20.4.0" options: api: description: - The REST API to call (eg I(cluster/software), I(svms/svm)). required: true type: str method: description: - The REST method to use. default: GET type: str query: description: - A list of dictionaries for the query parameters type: dict body: description: - A dictionary for the info parameter type: dict aliases: ['info'] vserver_name: description: - if provided, forces vserver tunneling. username identifies a cluster admin account. type: str vserver_uuid: description: - if provided, forces vserver tunneling. username identifies a cluster admin account. type: str hal_linking: description: - if true, HAL-encoded links are returned in the response. default: false type: bool wait_for_completion: description: - when true, POST/PATCH/DELETE can be handled synchronously and asynchronously. - if the response indicates that a job is in progress, the job status is checked periodically until is completes. - when false, the call returns immediately. type: bool default: false version_added: 21.14.0 files: description: - A dictionary for the parameters when using multipart/form-data. - This is very infrequently needed, but required to write a file (see examples) - When present, requests will automatically set the Content-Type header to multipart/form-data. type: dict version_added: 21.24.0 accept_header: description: - Value for the Accept request HTTP header. - This is very infrequently needed, but required to read a file (see examples). - For most cases, omit this field. Set it to "multipart/form-data" when expecting such a format. - By default the module is using "application/json" or "application/hal+json" when hal_linking is true. type: str version_added: 21.24.0 ''' EXAMPLES = """ - name: Ontap REST API hosts: localhost gather_facts: False collections: - netapp.ontap vars: login: &login hostname: "{{ admin_ip }}" username: "{{ admin_username }}" password: "{{ admin_password }}" https: true validate_certs: false svm_login: &svm_login hostname: "{{ svm_admin_ip }}" username: "{{ svm_admin_username }}" password: "{{ svm_admin_password }}" https: true validate_certs: false tasks: - name: run ontap REST API command as cluster admin na_ontap_restit: <<: *login api: cluster/software register: result - debug: var=result - assert: { that: result.status_code==200, quiet: True } - name: run ontap REST API command as cluster admin na_ontap_restit: <<: *login api: cluster/software query: fields: version register: result - debug: var=result - assert: { that: result.status_code==200, quiet: True } - name: run ontap REST API command as cluster admin na_ontap_restit: <<: *login api: svm/svms register: result - debug: var=result - assert: { that: result.status_code==200, quiet: True } - name: run ontap REST API command as cluster admin na_ontap_restit: <<: *login api: svm/svms query: fields: aggregates,cifs,nfs,uuid query_fields: name query: trident_svm hal_linking: true register: result - debug: var=result - name: run ontap REST API command as vsadmin na_ontap_restit: <<: *svm_login api: svm/svms register: result - debug: var=result - assert: { that: result.status_code==200, quiet: True } - name: run ontap REST API command as vserver tunneling na_ontap_restit: <<: *login api: storage/volumes vserver_name: ansibleSVM register: result - debug: var=result - assert: { that: result.status_code==200, quiet: True } - set_fact: uuid: "{{ result.response.records | json_query(get_uuid) }}" vars: get_uuid: "[? name=='deleteme_ln1'].uuid" - debug: var=uuid - name: run ontap REST API command as DELETE method with vserver tunneling na_ontap_restit: <<: *login api: "storage/volumes/{{ uuid[0] }}" method: DELETE vserver_name: ansibleSVM query: return_timeout: 60 register: result when: uuid|length == 1 - debug: var=result - assert: { that: result.skipped|default(false) or result.status_code|default(404) == 200, quiet: True } - name: run ontap REST API command as POST method with vserver tunneling na_ontap_restit: <<: *login api: storage/volumes method: POST vserver_name: ansibleSVM query: return_records: "true" return_timeout: 60 body: name: deleteme_ln1 aggregates: - name: aggr1 register: result - debug: var=result - assert: { that: result.status_code==201, quiet: True } - name: run ontap REST API command as DELETE method with vserver tunneling # delete test volume if present na_ontap_restit: <<: *login api: "storage/volumes/{{ result.response.records[0].uuid }}" method: DELETE vserver_name: ansibleSVM query: return_timeout: 60 register: result - debug: var=result - assert: { that: result.status_code==200, quiet: True } - name: create a file # assuming credentials are set using module_defaults na_ontap_restit: api: storage/volumes/f3c003cb-2974-11ed-b2f8-005056b38dae/files/laurent123.txt method: post files: {'data': 'some data'} - name: read a file # assuming credentials are set using module_defaults na_ontap_restit: api: storage/volumes/f3c003cb-2974-11ed-b2f8-005056b38dae/files/laurent123.txt method: get accept_header: "multipart/form-data" query: length: 100 # error cases - name: run ontap REST API command na_ontap_restit: <<: *login api: unknown/endpoint register: result ignore_errors: True - debug: var=result - assert: { that: result.status_code==404, quiet: True } """ RETURN = """ response: description: - If successful, a json dictionary returned by the REST API. - If the REST API was executed but failed, an empty dictionary. - Not present if the REST API call cannot be performed. returned: On success type: dict status_code: description: - The http status code. - When wait_for_completion is True, this is forced to 0. returned: Always type: str error_code: description: - If the REST API was executed but failed, the error code set by the REST API. - Not present if successful, or if the REST API call cannot be performed. returned: On error type: str error_message: description: - If the REST API was executed but failed, the error message set by the REST API. - Not present if successful, or if the REST API call cannot be performed. returned: On error type: str """ from ansible.module_utils.basic import AnsibleModule import ansible_collections.netapp.ontap.plugins.module_utils.netapp as netapp_utils from ansible_collections.netapp.ontap.plugins.module_utils.netapp import OntapRestAPI from ansible_collections.netapp.ontap.plugins.module_utils import rest_generic class NetAppONTAPRestAPI(object): ''' calls a REST API command ''' def __init__(self): self.argument_spec = netapp_utils.na_ontap_host_argument_spec() self.argument_spec.update(dict( api=dict(required=True, type='str'), method=dict(required=False, type='str', default='GET'), query=dict(required=False, type='dict'), body=dict(required=False, type='dict', aliases=['info']), vserver_name=dict(required=False, type='str'), vserver_uuid=dict(required=False, type='str'), hal_linking=dict(required=False, type='bool', default=False), wait_for_completion=dict(required=False, type='bool', default=False), # to support very infrequent form-data format files=dict(required=False, type='dict'), accept_header=dict(required=False, type='str'), )) self.module = AnsibleModule( argument_spec=self.argument_spec, supports_check_mode=True, ) parameters = self.module.params # set up state variables self.api = parameters['api'] self.method = parameters['method'] self.query = parameters['query'] self.body = parameters['body'] self.vserver_name = parameters['vserver_name'] self.vserver_uuid = parameters['vserver_uuid'] self.hal_linking = parameters['hal_linking'] self.wait_for_completion = parameters['wait_for_completion'] self.files = parameters['files'] self.accept_header = parameters['accept_header'] self.rest_api = OntapRestAPI(self.module) if self.accept_header is None: self.accept_header = 'application/hal+json' if self.hal_linking else 'application/json' def build_headers(self): return self.rest_api.build_headers(accept=self.accept_header, vserver_name=self.vserver_name, vserver_uuid=self.vserver_uuid) def fail_on_error(self, status, response, error): if error: if isinstance(error, dict): error_message = error.pop('message', None) error_code = error.pop('code', None) if not error: # we exhausted the dictionary error = 'check error_message and error_code for details.' else: error_message = error error_code = None msg = "Error when calling '%s': %s" % (self.api, str(error)) self.module.fail_json(msg=msg, status_code=status, response=response, error_message=error_message, error_code=error_code) def run_api(self): ''' calls the REST API ''' # TODO, log usage status, response, error = self.rest_api.send_request(self.method, self.api, self.query, self.body, self.build_headers(), self.files) self.fail_on_error(status, response, error) return status, response def run_api_async(self): ''' calls the REST API ''' # TODO, log usage args = [self.rest_api, self.api] kwargs = {} if self.method.upper() == 'POST': method = rest_generic.post_async kwargs['body'] = self.body kwargs['files'] = self.files elif self.method.upper() == 'PATCH': method = rest_generic.patch_async args.append(None) # uuid should be provided in the API kwargs['body'] = self.body kwargs['files'] = self.files elif self.method.upper() == 'DELETE': method = rest_generic.delete_async args.append(None) # uuid should be provided in the API else: self.module.warn('wait_for_completion ignored for %s method.' % self.method) return self.run_api() kwargs.update({ 'raw_error': True, 'headers': self.build_headers() }) if self.query: kwargs['query'] = self.query response, error = method(*args, **kwargs) self.fail_on_error(0, response, error) return 0, response def apply(self): ''' calls the api and returns json output ''' if self.module.check_mode: status_code, response = None, {'check_mode': 'would run %s %s' % (self.method, self.api)} elif self.wait_for_completion: status_code, response = self.run_api_async() else: status_code, response = self.run_api() self.module.exit_json(changed=True, status_code=status_code, response=response) def main(): """ Execute action from playbook """ restapi = NetAppONTAPRestAPI() restapi.apply() if __name__ == '__main__': main()