|
| 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