From 2d6c01005df63ba2f75fb25867ac57c01f3f235e Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 29 Oct 2018 12:27:03 +0000 Subject: [PATCH] New facts module: Memset Memstore (#42387) * initial commit of facts module to return usage of a Memstore cloudstorage product * switch API wrapper to use basic auth instead of passing the api_key in the body * add integration tests (disabled until we have a mock API to test against) * bump ansible release version to 2.8 --- lib/ansible/module_utils/memset.py | 5 +- .../cloud/memset/memset_memstore_facts.py | 173 ++++++++++++++++++ .../targets/memset_memstore_facts/aliases | 1 + .../memset_memstore_facts/meta/main.yml | 1 + .../memset_memstore_facts/tasks/main.yml | 27 +++ 5 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 lib/ansible/modules/cloud/memset/memset_memstore_facts.py create mode 100644 test/integration/targets/memset_memstore_facts/aliases create mode 100644 test/integration/targets/memset_memstore_facts/meta/main.yml create mode 100644 test/integration/targets/memset_memstore_facts/tasks/main.yml diff --git a/lib/ansible/module_utils/memset.py b/lib/ansible/module_utils/memset.py index 13a4cbf40ba..51dce0c6907 100644 --- a/lib/ansible/module_utils/memset.py +++ b/lib/ansible/module_utils/memset.py @@ -60,7 +60,6 @@ def memset_api_call(api_key, api_method, payload=None): else: payload = payload.copy() - payload['api_key'] = api_key # set some sane defaults has_failed = False msg = None @@ -71,7 +70,7 @@ def memset_api_call(api_key, api_method, payload=None): api_uri = '{0}{1}/' . format(api_uri_base, api_method) try: - resp = open_url(api_uri, data=data, headers=headers, method="POST") + resp = open_url(api_uri, data=data, headers=headers, method="POST", force_basic_auth=True, url_username=api_key) response.content = resp.read().decode('utf-8') response.status_code = resp.getcode() except urllib_error.HTTPError as e: @@ -89,8 +88,6 @@ def memset_api_call(api_key, api_method, payload=None): else: msg = "Memset API returned an error ({0}, {1})." . format(response.json()['error_type'], response.json()['error']) - del payload['api_key'] - if msg is None: msg = response.json() diff --git a/lib/ansible/modules/cloud/memset/memset_memstore_facts.py b/lib/ansible/modules/cloud/memset/memset_memstore_facts.py new file mode 100644 index 00000000000..d3f97b38be6 --- /dev/null +++ b/lib/ansible/modules/cloud/memset/memset_memstore_facts.py @@ -0,0 +1,173 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright (c) 2018, Simon Weald +# 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': 'community' +} + +DOCUMENTATION = ''' +--- +module: memset_memstore_facts +author: "Simon Weald (@analbeard)" +version_added: "2.8" +short_description: Retrieve Memstore product usage information. +notes: + - An API key generated via the Memset customer control panel is needed with the + following minimum scope - I(memstore.usage). +description: + - Retrieve Memstore product usage information. +options: + api_key: + required: true + description: + - The API key obtained from the Memset control panel. + name: + required: true + description: + - The Memstore product name (i.e. C(mstestyaa1)). +''' + +EXAMPLES = ''' +- name: get usage for mstestyaa1 + memset_memstore_facts: + name: mstestyaa1 + api_key: 5eb86c9896ab03919abcf03857163741 + delegate_to: localhost +''' + +RETURN = ''' +--- +memset_api: + description: Info from the Memset API + returned: always + type: complex + contains: + cdn_bandwidth: + description: Dictionary of CDN bandwidth facts + returned: always + type: complex + contains: + bytes_out: + description: Outbound CDN bandwidth for the last 24 hours in bytes + returned: always + type: integer + sample: 1000 + requests: + description: Number of requests in the last 24 hours + returned: always + type: integer + sample: 10 + bytes_in: + description: Inbound CDN bandwidth for the last 24 hours in bytes + returned: always + type: integer + sample: 1000 + containers: + description: Number of containers + returned: always + type: integer + sample: 10 + bytes: + description: Space used in bytes + returned: always + type: integer + sample: 3860997965 + objs: + description: Number of objects + returned: always + type: integer + sample: 1000 + bandwidth: + description: Dictionary of CDN bandwidth facts + returned: always + type: complex + contains: + bytes_out: + description: Outbound bandwidth for the last 24 hours in bytes + returned: always + type: integer + sample: 1000 + requests: + description: Number of requests in the last 24 hours + returned: always + type: integer + sample: 10 + bytes_in: + description: Inbound bandwidth for the last 24 hours in bytes + returned: always + type: integer + sample: 1000 +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.memset import memset_api_call + + +def get_facts(args=None): + ''' + Performs a simple API call and returns a JSON blob. + ''' + retvals, payload = dict(), dict() + has_changed, has_failed = False, False + msg, stderr, memset_api = None, None, None + + payload['name'] = args['name'] + + api_method = 'memstore.usage' + has_failed, msg, response = memset_api_call(api_key=args['api_key'], api_method=api_method, payload=payload) + + if has_failed: + # this is the first time the API is called; incorrect credentials will + # manifest themselves at this point so we need to ensure the user is + # informed of the reason. + retvals['failed'] = has_failed + retvals['msg'] = msg + retvals['stderr'] = "API returned an error: {0}" . format(response.status_code) + return(retvals) + + # we don't want to return the same thing twice + msg = None + memset_api = response.json() + + retvals['changed'] = has_changed + retvals['failed'] = has_failed + for val in ['msg', 'memset_api']: + if val is not None: + retvals[val] = eval(val) + + return(retvals) + + +def main(): + global module + module = AnsibleModule( + argument_spec=dict( + api_key=dict(required=True, type='str', no_log=True), + name=dict(required=True, type='str') + ), + supports_check_mode=False + ) + + # populate the dict with the user-provided vars. + args = dict() + for key, arg in module.params.items(): + args[key] = arg + + retvals = get_facts(args) + + if retvals['failed']: + module.fail_json(**retvals) + else: + module.exit_json(**retvals) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/memset_memstore_facts/aliases b/test/integration/targets/memset_memstore_facts/aliases new file mode 100644 index 00000000000..ad7ccf7ada2 --- /dev/null +++ b/test/integration/targets/memset_memstore_facts/aliases @@ -0,0 +1 @@ +unsupported diff --git a/test/integration/targets/memset_memstore_facts/meta/main.yml b/test/integration/targets/memset_memstore_facts/meta/main.yml new file mode 100644 index 00000000000..73b314ff7c7 --- /dev/null +++ b/test/integration/targets/memset_memstore_facts/meta/main.yml @@ -0,0 +1 @@ +--- \ No newline at end of file diff --git a/test/integration/targets/memset_memstore_facts/tasks/main.yml b/test/integration/targets/memset_memstore_facts/tasks/main.yml new file mode 100644 index 00000000000..b389e025782 --- /dev/null +++ b/test/integration/targets/memset_memstore_facts/tasks/main.yml @@ -0,0 +1,27 @@ +--- +- name: query API with invalid API key + local_action: + module: memset_memstore_facts + api_key: 'wa9aerahhie0eekee9iaphoorovooyia' + name: 'mstestyaa1' + ignore_errors: true + register: result + +- name: check API response with invalid API key + assert: + that: + - "'Memset API returned a 403 response (ApiErrorForbidden, Bad api_key)' in result.msg" + - result is not successful + +- name: request memstore facts + local_action: + module: memset_memstore_facts + api_key: "{{ api_key }}" + name: 'mstestyaa1' + register: result + +- name: check the request succeeded + assert: + that: + - result is not changed + - result is successful