From 5cf9644003eeb1936f9c9f269e6f979fcc913b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20L=C3=A9one?= Date: Fri, 31 Aug 2018 20:02:40 +0200 Subject: [PATCH] Add an user facts module for Online (#44709) --- lib/ansible/module_utils/online.py | 116 ++++++++++++++++++ lib/ansible/modules/cloud/online/__init__.py | 0 .../modules/cloud/online/online_user_facts.py | 75 +++++++++++ .../utils/module_docs_fragments/online.py | 36 ++++++ test/legacy/online.yml | 7 ++ .../roles/online_user_facts/tasks/main.yml | 14 +++ 6 files changed, 248 insertions(+) create mode 100644 lib/ansible/module_utils/online.py create mode 100644 lib/ansible/modules/cloud/online/__init__.py create mode 100644 lib/ansible/modules/cloud/online/online_user_facts.py create mode 100644 lib/ansible/utils/module_docs_fragments/online.py create mode 100644 test/legacy/online.yml create mode 100644 test/legacy/roles/online_user_facts/tasks/main.yml diff --git a/lib/ansible/module_utils/online.py b/lib/ansible/module_utils/online.py new file mode 100644 index 00000000000..33d9a79f104 --- /dev/null +++ b/lib/ansible/module_utils/online.py @@ -0,0 +1,116 @@ +import json +import sys + +from ansible.module_utils.basic import env_fallback +from ansible.module_utils.urls import fetch_url + + +def online_argument_spec(): + return dict( + api_token=dict(required=True, fallback=(env_fallback, ['ONLINE_TOKEN', 'ONLINE_API_KEY', 'ONLINE_OAUTH_TOKEN', 'ONLINE_API_TOKEN']), + no_log=True, aliases=['oauth_token']), + api_url=dict(fallback=(env_fallback, ['ONLINE_API_URL']), default='https://api.online.net/api/v1', aliases=['base_url']), + api_timeout=dict(type='int', default=30, aliases=['timeout']), + validate_certs=dict(default=True, type='bool'), + ) + + +class OnlineException(Exception): + + def __init__(self, message): + self.message = message + + +class Response(object): + + def __init__(self, resp, info): + self.body = None + if resp: + self.body = resp.read() + self.info = info + + @property + def json(self): + if not self.body: + if "body" in self.info: + return json.loads(self.info["body"]) + return None + try: + return json.loads(self.body) + except ValueError: + return None + + @property + def status_code(self): + return self.info["status"] + + @property + def ok(self): + return self.status_code in (200, 201, 202, 204) + + +class Online(object): + + def __init__(self, module): + self.module = module + self.headers = { + 'Authorization': "Bearer %s" % self.module.params.get('api_token'), + 'User-Agent': self.get_user_agent_string(module), + 'Content-type': 'application/json', + } + self.name = None + + def get_resources(self): + results = self.get('/%s' % self.name) + if not results.ok: + raise OnlineException('Error fetching {0} ({1}) [{2}: {3}]'.format( + self.name, '%s/%s' % (self.module.params.get('api_url'), self.name), + results.status_code, results.json['message'] + )) + + return results.json + + def _url_builder(self, path): + if path[0] == '/': + path = path[1:] + return '%s/%s' % (self.module.params.get('api_url'), path) + + def send(self, method, path, data=None, headers=None): + url = self._url_builder(path) + data = self.module.jsonify(data) + + if headers is not None: + self.headers.update(headers) + + resp, info = fetch_url( + self.module, url, data=data, headers=self.headers, method=method, + timeout=self.module.params.get('api_timeout') + ) + + # Exceptions in fetch_url may result in a status -1, the ensures a proper error to the user in all cases + if info['status'] == -1: + self.module.fail_json(msg=info['msg']) + + return Response(resp, info) + + @staticmethod + def get_user_agent_string(module): + return "ansible %s Python %s" % (module.ansible_version, sys.version.split(' ')[0]) + + def get(self, path, data=None, headers=None): + return self.send('GET', path, data, headers) + + def put(self, path, data=None, headers=None): + return self.send('PUT', path, data, headers) + + def post(self, path, data=None, headers=None): + return self.send('POST', path, data, headers) + + def delete(self, path, data=None, headers=None): + return self.send('DELETE', path, data, headers) + + def patch(self, path, data=None, headers=None): + return self.send("PATCH", path, data, headers) + + def update(self, path, data=None, headers=None): + return self.send("UPDATE", path, data, headers) diff --git a/lib/ansible/modules/cloud/online/__init__.py b/lib/ansible/modules/cloud/online/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/ansible/modules/cloud/online/online_user_facts.py b/lib/ansible/modules/cloud/online/online_user_facts.py new file mode 100644 index 00000000000..dfda4121446 --- /dev/null +++ b/lib/ansible/modules/cloud/online/online_user_facts.py @@ -0,0 +1,75 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# 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 = r''' +--- +module: online_user_facts +short_description: Gather facts about Online user. +description: + - Gather facts about the user. +version_added: "2.7" +author: + - "Remy Leone (@sieben)" +extends_documentation_fragment: online +''' + +EXAMPLES = r''' +- name: Gather Online user facts + online_user_facts: +''' + +RETURN = r''' +--- +online_user_facts: + description: Response from Online API + returned: success + type: complex + contains: + "online_user_facts": { + "company": "foobar LLC", + "email": "foobar@example.com", + "first_name": "foo", + "id": 42, + "last_name": "bar", + "login": "foobar" + } +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.online import ( + Online, OnlineException, online_argument_spec +) + + +class OnlineUserFacts(Online): + + def __init__(self, module): + super(OnlineUserFacts, self).__init__(module) + self.name = 'user' + + +def main(): + module = AnsibleModule( + argument_spec=online_argument_spec(), + supports_check_mode=True, + ) + + try: + module.exit_json( + ansible_facts={'online_user_facts': OnlineUserFacts(module).get_resources()} + ) + except OnlineException as exc: + module.fail_json(msg=exc.message) + + +if __name__ == '__main__': + main() diff --git a/lib/ansible/utils/module_docs_fragments/online.py b/lib/ansible/utils/module_docs_fragments/online.py new file mode 100644 index 00000000000..32f7e35e67c --- /dev/null +++ b/lib/ansible/utils/module_docs_fragments/online.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +class ModuleDocFragment(object): + + # Standard documentation fragment + DOCUMENTATION = ''' +options: + api_token: + description: + - Online OAuth token. + aliases: ['oauth_token'] + api_url: + description: + - Online API URL + default: 'https://api.online.net/api/v1' + aliases: ['base_url'] + api_timeout: + description: + - HTTP timeout to Online API in seconds. + default: 30 + aliases: ['timeout'] + validate_certs: + description: + - Validate SSL certs of the Online API. + default: yes + type: bool +notes: + - Also see the API documentation on U(https://console.online.net/en/api/) + - If C(api_token) is not set within the module, the following + environment variables can be used in decreasing order of precedence + C(ONLINE_TOKEN), C(ONLINE_API_KEY), C(ONLINE_OAUTH_TOKEN), C(ONLINE_API_TOKEN) + - If one wants to use a different C(api_url) one can also set the C(ONLINE_API_URL) + environment variable. +''' diff --git a/test/legacy/online.yml b/test/legacy/online.yml new file mode 100644 index 00000000000..108663c269e --- /dev/null +++ b/test/legacy/online.yml @@ -0,0 +1,7 @@ +--- +- hosts: localhost + gather_facts: no + connection: local + + roles: + - { role: online_user_facts, tags: test_online_user_facts } diff --git a/test/legacy/roles/online_user_facts/tasks/main.yml b/test/legacy/roles/online_user_facts/tasks/main.yml new file mode 100644 index 00000000000..6603ef4cc69 --- /dev/null +++ b/test/legacy/roles/online_user_facts/tasks/main.yml @@ -0,0 +1,14 @@ +# ONLINE_TOKEN='XXX' ansible-playbook ./test/legacy/online.yml --tags test_online_user_facts + +- name: Get user information and register it in a variable + online_user_facts: + register: user + +- name: Display user variable + debug: + var: user + +- name: Ensure retrieval of user facts is success + assert: + that: + - user is success