Skip to content

Commit 7bedacf

Browse files
add api lookup
1 parent 5a2c329 commit 7bedacf

File tree

2 files changed

+214
-0
lines changed

2 files changed

+214
-0
lines changed

plugins/lookup/api.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# (c) 2020 Ansible Project
2+
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
3+
from __future__ import absolute_import, division, print_function
4+
5+
__metaclass__ = type
6+
7+
DOCUMENTATION = """
8+
name: api
9+
author: Lorenzo Tanganelli (@tanganellilore)
10+
short_description: Iteract with the Cloudstack API via lookup
11+
requirements:
12+
- None
13+
description:
14+
- Returns GET requests from the Cloudstack API.
15+
options:
16+
_terms:
17+
description:
18+
- The endpoint to query, i.e. listUserData, listVirtualMachines, etc.
19+
required: True
20+
query_params:
21+
description:
22+
- The query parameters to search for in the form of key/value pairs.
23+
type: dict
24+
required: False
25+
extends_documentation_fragment:
26+
- ngine_io.cloudstack.cloudstack
27+
notes:
28+
- If the query is not filtered properly this can cause a performance impact.
29+
"""
30+
31+
EXAMPLES = """
32+
- name: List all UserData from the API
33+
set_fact:
34+
controller_settings: "{{ lookup('ngine_io.cloudstack.api', 'listUserData', query_params={ 'listall': true }) }}"
35+
36+
- name: List all Virtual Machines from the API
37+
set_fact:
38+
virtual_machines: "{{ lookup('ngine_io.cloudstack.api', 'listVirtualMachines') }}"
39+
40+
- name: List specific Virtual Machines from the API
41+
set_fact:
42+
virtual_machines: "{{ lookup('ngine_io.cloudstack.api', 'listVirtualMachines', query_params={ 'name': 'myvmname' }) }}"
43+
"""
44+
45+
RETURN = """
46+
_raw:
47+
description:
48+
- Response from the API
49+
type: dict
50+
returned: on successful request
51+
"""
52+
53+
from ansible.plugins.lookup import LookupBase
54+
from ansible.errors import AnsibleError
55+
from ansible.module_utils._text import to_native
56+
from ansible.utils.display import Display
57+
58+
from ..module_utils.cloudstack_api import AnsibleCloudStackAPI
59+
60+
class LookupModule(LookupBase):
61+
display = Display()
62+
63+
def handle_error(self, **kwargs):
64+
raise AnsibleError(to_native(kwargs.get('msg')))
65+
66+
def warn_callback(self, warning):
67+
self.display.warning(warning)
68+
69+
def run(self, terms, variables=None, **kwargs):
70+
if len(terms) != 1:
71+
raise AnsibleError('You must pass exactly one endpoint to query')
72+
73+
self.set_options(direct=kwargs)
74+
75+
module = AnsibleCloudStackAPI(argument_spec={}, direct_params=kwargs, error_callback=self.handle_error, warn_callback=self.warn_callback)
76+
77+
args = {}
78+
if self.get_option('query_params'):
79+
args.update(self.get_option('query_params', {}))
80+
81+
res = module.query_api(terms[0], **args)
82+
83+
if res is None:
84+
return []
85+
if type(res) is list:
86+
return res
87+
return [res]
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright (c) 2024, Lorenzo Tanganelli
3+
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
4+
5+
from __future__ import absolute_import, division, print_function
6+
7+
__metaclass__ = type
8+
9+
10+
import os
11+
import re
12+
import sys
13+
import traceback
14+
15+
from ansible.module_utils._text import to_native
16+
from ansible.module_utils.basic import missing_required_lib, AnsibleModule
17+
18+
CS_IMP_ERR = None
19+
try:
20+
from cs import CloudStack, CloudStackException
21+
HAS_LIB_CS = True
22+
except ImportError:
23+
CS_IMP_ERR = traceback.format_exc()
24+
HAS_LIB_CS = False
25+
26+
27+
if sys.version_info > (3,):
28+
long = int
29+
30+
31+
class AnsibleCloudStackAPI(AnsibleModule):
32+
33+
error_callback = None
34+
warn_callback = None
35+
AUTH_ARGSPEC = dict(
36+
api_key=os.getenv('CLOUDSTACK_KEY'),
37+
api_secret=os.getenv('CLOUDSTACK_SECRET'),
38+
api_url=os.getenv('CLOUDSTACK_ENDPOINT'),
39+
api_http_method=os.getenv('CLOUDSTACK_METHOD', 'get'),
40+
api_timeout=os.getenv('CLOUDSTACK_TIMEOUT', 10),
41+
api_verify_ssl_cert=os.getenv('CLOUDSTACK_VERIFY'),
42+
validate_certs=os.getenv('CLOUDSTACK_DANGEROUS_NO_TLS_VERIFY', True),
43+
)
44+
45+
def __init__(self, argument_spec=None, direct_params=None, error_callback=None, warn_callback=None, **kwargs):
46+
47+
if not HAS_LIB_CS:
48+
self.fail_json(msg=missing_required_lib('cs'), exception=CS_IMP_ERR)
49+
50+
full_argspec = {}
51+
full_argspec.update(AnsibleCloudStackAPI.AUTH_ARGSPEC)
52+
full_argspec.update(argument_spec)
53+
kwargs['supports_check_mode'] = True
54+
55+
self.error_callback = error_callback
56+
self.warn_callback = warn_callback
57+
58+
self._cs = None
59+
self.result = {}
60+
61+
if direct_params is not None:
62+
for param, value in full_argspec.items():
63+
if param in direct_params:
64+
setattr(self, param, direct_params[param])
65+
else:
66+
setattr(self, param, value)
67+
else:
68+
super(AnsibleCloudStackAPI, self).__init__(argument_spec=full_argspec, **kwargs)
69+
70+
# Perform some basic validation
71+
if not re.match('^https{0,1}://', self.api_url):
72+
self.api_url = "https://{0}".format(self.api_url)
73+
74+
75+
def fail_json(self, **kwargs):
76+
if self.error_callback:
77+
self.error_callback(**kwargs)
78+
else:
79+
super().fail_json(**kwargs)
80+
81+
def exit_json(self, **kwargs):
82+
super().exit_json(**kwargs)
83+
84+
@property
85+
def cs(self):
86+
if self._cs is None:
87+
api_config = self.get_api_config()
88+
self._cs = CloudStack(**api_config)
89+
return self._cs
90+
91+
def get_api_config(self):
92+
api_config = {
93+
'endpoint': self.api_url,
94+
'key': self.api_key,
95+
'secret': self.api_secret,
96+
'timeout': self.api_timeout,
97+
'method': self.api_http_method,
98+
'verify': self.api_verify_ssl_cert,
99+
'dangerous_no_tls_verify': not self.validate_certs,
100+
}
101+
102+
# self.result.update({
103+
# 'api_url': api_config['endpoint'],
104+
# 'api_key': api_config['key'],
105+
# 'api_timeout': int(api_config['timeout']),
106+
# 'api_http_method': api_config['method'],
107+
# 'api_verify_ssl_cert': api_config['verify'],
108+
# 'validate_certs': not api_config['dangerous_no_tls_verify'],
109+
# })
110+
return api_config
111+
112+
def query_api(self, command, **args):
113+
114+
try:
115+
res = getattr(self.cs, command)(**args)
116+
117+
if 'errortext' in res:
118+
self.fail_json(msg="Failed: '%s'" % res['errortext'])
119+
120+
except CloudStackException as e:
121+
self.fail_json(msg='CloudStackException: %s' % to_native(e))
122+
123+
except Exception as e:
124+
self.fail_json(msg=to_native(e))
125+
126+
# res.update({'params': self.result, 'query_params': args})
127+
return res

0 commit comments

Comments
 (0)