mirror of
https://github.com/ansible/awx.git
synced 2024-10-30 05:25:29 +03:00
Adding import/export awx kit features
Changed library structure Origional TowerModule becomes TowerLegacyModule TowerModule from tower_api becomes TowerAPIModule A real base TowerModule is created in tower_module.py A new TowerAWXKitModule is created in tower_awxkit TowerAWXKitModule and TowerAPIModule are child classes of TowerModule
This commit is contained in:
parent
383f8aa8f9
commit
40f6741474
@ -72,7 +72,7 @@ from ansible.errors import AnsibleParserError, AnsibleOptionsError
|
||||
from ansible.plugins.inventory import BaseInventoryPlugin
|
||||
from ansible.config.manager import ensure_type
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def handle_error(**kwargs):
|
||||
@ -104,12 +104,12 @@ class InventoryModule(BaseInventoryPlugin):
|
||||
|
||||
# Defer processing of params to logic shared with the modules
|
||||
module_params = {}
|
||||
for plugin_param, module_param in TowerModule.short_params.items():
|
||||
for plugin_param, module_param in TowerAPIModule.short_params.items():
|
||||
opt_val = self.get_option(plugin_param)
|
||||
if opt_val is not None:
|
||||
module_params[module_param] = opt_val
|
||||
|
||||
module = TowerModule(
|
||||
module = TowerAPIModule(
|
||||
argument_spec={}, direct_params=module_params,
|
||||
error_callback=handle_error, warn_callback=self.warn_callback
|
||||
)
|
||||
|
@ -115,7 +115,7 @@ from ansible.plugins.lookup import LookupBase
|
||||
from ansible.errors import AnsibleError
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.utils.display import Display
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
class LookupModule(LookupBase):
|
||||
@ -133,13 +133,13 @@ class LookupModule(LookupBase):
|
||||
|
||||
# Defer processing of params to logic shared with the modules
|
||||
module_params = {}
|
||||
for plugin_param, module_param in TowerModule.short_params.items():
|
||||
for plugin_param, module_param in TowerAPIModule.short_params.items():
|
||||
opt_val = self.get_option(plugin_param)
|
||||
if opt_val is not None:
|
||||
module_params[module_param] = opt_val
|
||||
|
||||
# Create our module
|
||||
module = TowerModule(
|
||||
module = TowerAPIModule(
|
||||
argument_spec={}, direct_params=module_params,
|
||||
error_callback=self.handle_error, warn_callback=self.warn_callback
|
||||
)
|
||||
|
@ -1,37 +1,17 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
||||
from . tower_module import TowerModule
|
||||
from ansible.module_utils.urls import Request, SSLValidationError, ConnectionError
|
||||
from ansible.module_utils.six import PY2, string_types
|
||||
from ansible.module_utils.six.moves import StringIO
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlparse, urlencode
|
||||
from ansible.module_utils.six import PY2
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlencode
|
||||
from ansible.module_utils.six.moves.urllib.error import HTTPError
|
||||
from ansible.module_utils.six.moves.http_cookiejar import CookieJar
|
||||
from ansible.module_utils.six.moves.configparser import ConfigParser, NoOptionError
|
||||
from socket import gethostbyname
|
||||
import re
|
||||
from json import loads, dumps
|
||||
from os.path import isfile, expanduser, split, join, exists, isdir
|
||||
from os import access, R_OK, getcwd
|
||||
from distutils.util import strtobool
|
||||
|
||||
try:
|
||||
import yaml
|
||||
HAS_YAML = True
|
||||
except ImportError:
|
||||
HAS_YAML = False
|
||||
|
||||
|
||||
class ConfigFileException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ItemNotDefined(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class TowerModule(AnsibleModule):
|
||||
class TowerAPIModule(TowerModule):
|
||||
# TODO: Move the collection version check into tower_module.py
|
||||
# This gets set by the make process so whatever is in here is irrelevant
|
||||
_COLLECTION_VERSION = "0.0.1-devel"
|
||||
_COLLECTION_TYPE = "awx"
|
||||
@ -41,197 +21,15 @@ class TowerModule(AnsibleModule):
|
||||
'awx': 'AWX',
|
||||
'tower': 'Red Hat Ansible Tower',
|
||||
}
|
||||
url = None
|
||||
AUTH_ARGSPEC = dict(
|
||||
tower_host=dict(required=False, fallback=(env_fallback, ['TOWER_HOST'])),
|
||||
tower_username=dict(required=False, fallback=(env_fallback, ['TOWER_USERNAME'])),
|
||||
tower_password=dict(no_log=True, required=False, fallback=(env_fallback, ['TOWER_PASSWORD'])),
|
||||
validate_certs=dict(type='bool', aliases=['tower_verify_ssl'], required=False, fallback=(env_fallback, ['TOWER_VERIFY_SSL'])),
|
||||
tower_oauthtoken=dict(type='raw', no_log=True, required=False, fallback=(env_fallback, ['TOWER_OAUTH_TOKEN'])),
|
||||
tower_config_file=dict(type='path', required=False, default=None),
|
||||
)
|
||||
short_params = {
|
||||
'host': 'tower_host',
|
||||
'username': 'tower_username',
|
||||
'password': 'tower_password',
|
||||
'verify_ssl': 'validate_certs',
|
||||
'oauth_token': 'tower_oauthtoken',
|
||||
}
|
||||
host = '127.0.0.1'
|
||||
username = None
|
||||
password = None
|
||||
verify_ssl = True
|
||||
oauth_token = None
|
||||
oauth_token_id = None
|
||||
session = None
|
||||
cookie_jar = CookieJar()
|
||||
authenticated = False
|
||||
config_name = 'tower_cli.cfg'
|
||||
ENCRYPTED_STRING = "$encrypted$"
|
||||
version_checked = False
|
||||
error_callback = None
|
||||
warn_callback = None
|
||||
|
||||
def __init__(self, argument_spec, direct_params=None, error_callback=None, warn_callback=None, **kwargs):
|
||||
full_argspec = {}
|
||||
full_argspec.update(TowerModule.AUTH_ARGSPEC)
|
||||
full_argspec.update(argument_spec)
|
||||
kwargs['supports_check_mode'] = True
|
||||
|
||||
self.error_callback = error_callback
|
||||
self.warn_callback = warn_callback
|
||||
|
||||
self.json_output = {'changed': False}
|
||||
|
||||
if direct_params is not None:
|
||||
self.params = direct_params
|
||||
else:
|
||||
super(TowerModule, self).__init__(argument_spec=full_argspec, **kwargs)
|
||||
|
||||
self.load_config_files()
|
||||
|
||||
# Parameters specified on command line will override settings in any config
|
||||
for short_param, long_param in self.short_params.items():
|
||||
direct_value = self.params.get(long_param)
|
||||
if direct_value is not None:
|
||||
setattr(self, short_param, direct_value)
|
||||
|
||||
# Perform magic depending on whether tower_oauthtoken is a string or a dict
|
||||
if self.params.get('tower_oauthtoken'):
|
||||
token_param = self.params.get('tower_oauthtoken')
|
||||
if type(token_param) is dict:
|
||||
if 'token' in token_param:
|
||||
self.oauth_token = self.params.get('tower_oauthtoken')['token']
|
||||
else:
|
||||
self.fail_json(msg="The provided dict in tower_oauthtoken did not properly contain the token entry")
|
||||
elif isinstance(token_param, string_types):
|
||||
self.oauth_token = self.params.get('tower_oauthtoken')
|
||||
else:
|
||||
error_msg = "The provided tower_oauthtoken type was not valid ({0}). Valid options are str or dict.".format(type(token_param).__name__)
|
||||
self.fail_json(msg=error_msg)
|
||||
|
||||
# Perform some basic validation
|
||||
if not re.match('^https{0,1}://', self.host):
|
||||
self.host = "https://{0}".format(self.host)
|
||||
|
||||
# Try to parse the hostname as a url
|
||||
try:
|
||||
self.url = urlparse(self.host)
|
||||
except Exception as e:
|
||||
self.fail_json(msg="Unable to parse tower_host as a URL ({1}): {0}".format(self.host, e))
|
||||
|
||||
# Try to resolve the hostname
|
||||
hostname = self.url.netloc.split(':')[0]
|
||||
try:
|
||||
gethostbyname(hostname)
|
||||
except Exception as e:
|
||||
self.fail_json(msg="Unable to resolve tower_host ({1}): {0}".format(hostname, e))
|
||||
|
||||
super(TowerAPIModule, self).__init__(argument_spec=argument_spec, direct_params=direct_params, error_callback=error_callback, warn_callback=warn_callback, **kwargs)
|
||||
self.session = Request(cookies=CookieJar(), validate_certs=self.verify_ssl)
|
||||
|
||||
def load_config_files(self):
|
||||
# Load configs like TowerCLI would have from least import to most
|
||||
config_files = ['/etc/tower/tower_cli.cfg', join(expanduser("~"), ".{0}".format(self.config_name))]
|
||||
local_dir = getcwd()
|
||||
config_files.append(join(local_dir, self.config_name))
|
||||
while split(local_dir)[1]:
|
||||
local_dir = split(local_dir)[0]
|
||||
config_files.insert(2, join(local_dir, ".{0}".format(self.config_name)))
|
||||
|
||||
# If we have a specified tower config, load it
|
||||
if self.params.get('tower_config_file'):
|
||||
duplicated_params = [
|
||||
fn for fn in self.AUTH_ARGSPEC
|
||||
if fn != 'tower_config_file' and self.params.get(fn) is not None
|
||||
]
|
||||
if duplicated_params:
|
||||
self.warn((
|
||||
'The parameter(s) {0} were provided at the same time as tower_config_file. '
|
||||
'Precedence may be unstable, we suggest either using config file or params.'
|
||||
).format(', '.join(duplicated_params)))
|
||||
try:
|
||||
# TODO: warn if there are conflicts with other params
|
||||
self.load_config(self.params.get('tower_config_file'))
|
||||
except ConfigFileException as cfe:
|
||||
# Since we were told specifically to load this we want it to fail if we have an error
|
||||
self.fail_json(msg=cfe)
|
||||
else:
|
||||
for config_file in config_files:
|
||||
if exists(config_file) and not isdir(config_file):
|
||||
# Only throw a formatting error if the file exists and is not a directory
|
||||
try:
|
||||
self.load_config(config_file)
|
||||
except ConfigFileException:
|
||||
self.fail_json(msg='The config file {0} is not properly formatted'.format(config_file))
|
||||
|
||||
def load_config(self, config_path):
|
||||
# Validate the config file is an actual file
|
||||
if not isfile(config_path):
|
||||
raise ConfigFileException('The specified config file does not exist')
|
||||
|
||||
if not access(config_path, R_OK):
|
||||
raise ConfigFileException("The specified config file cannot be read")
|
||||
|
||||
# Read in the file contents:
|
||||
with open(config_path, 'r') as f:
|
||||
config_string = f.read()
|
||||
|
||||
# First try to yaml load the content (which will also load json)
|
||||
try:
|
||||
try_config_parsing = True
|
||||
if HAS_YAML:
|
||||
try:
|
||||
config_data = yaml.load(config_string, Loader=yaml.SafeLoader)
|
||||
# If this is an actual ini file, yaml will return the whole thing as a string instead of a dict
|
||||
if type(config_data) is not dict:
|
||||
raise AssertionError("The yaml config file is not properly formatted as a dict.")
|
||||
try_config_parsing = False
|
||||
|
||||
except(AttributeError, yaml.YAMLError, AssertionError):
|
||||
try_config_parsing = True
|
||||
|
||||
if try_config_parsing:
|
||||
# TowerCLI used to support a config file with a missing [general] section by prepending it if missing
|
||||
if '[general]' not in config_string:
|
||||
config_string = '[general]\n{0}'.format(config_string)
|
||||
|
||||
config = ConfigParser()
|
||||
|
||||
try:
|
||||
placeholder_file = StringIO(config_string)
|
||||
# py2 ConfigParser has readfp, that has been deprecated in favor of read_file in py3
|
||||
# This "if" removes the deprecation warning
|
||||
if hasattr(config, 'read_file'):
|
||||
config.read_file(placeholder_file)
|
||||
else:
|
||||
config.readfp(placeholder_file)
|
||||
|
||||
# If we made it here then we have values from reading the ini file, so let's pull them out into a dict
|
||||
config_data = {}
|
||||
for honorred_setting in self.short_params:
|
||||
try:
|
||||
config_data[honorred_setting] = config.get('general', honorred_setting)
|
||||
except NoOptionError:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
raise ConfigFileException("An unknown exception occured trying to ini load config file: {0}".format(e))
|
||||
|
||||
except Exception as e:
|
||||
raise ConfigFileException("An unknown exception occured trying to load config file: {0}".format(e))
|
||||
|
||||
# If we made it here, we have a dict which has values in it from our config, any final settings logic can be performed here
|
||||
for honorred_setting in self.short_params:
|
||||
if honorred_setting in config_data:
|
||||
# Veriffy SSL must be a boolean
|
||||
if honorred_setting == 'verify_ssl':
|
||||
if type(config_data[honorred_setting]) is str:
|
||||
setattr(self, honorred_setting, strtobool(config_data[honorred_setting]))
|
||||
else:
|
||||
setattr(self, honorred_setting, bool(config_data[honorred_setting]))
|
||||
else:
|
||||
setattr(self, honorred_setting, config_data[honorred_setting])
|
||||
|
||||
@staticmethod
|
||||
def param_to_endpoint(name):
|
||||
exceptions = {
|
||||
@ -650,13 +448,13 @@ class TowerModule(AnsibleModule):
|
||||
"""
|
||||
if isinstance(obj, dict):
|
||||
for val in obj.values():
|
||||
if TowerModule.has_encrypted_values(val):
|
||||
if TowerAPIModule.has_encrypted_values(val):
|
||||
return True
|
||||
elif isinstance(obj, list):
|
||||
for val in obj:
|
||||
if TowerModule.has_encrypted_values(val):
|
||||
if TowerAPIModule.has_encrypted_values(val):
|
||||
return True
|
||||
elif obj == TowerModule.ENCRYPTED_STRING:
|
||||
elif obj == TowerAPIModule.ENCRYPTED_STRING:
|
||||
return True
|
||||
return False
|
||||
|
||||
@ -678,10 +476,9 @@ class TowerModule(AnsibleModule):
|
||||
# This will exit from the module on its own
|
||||
# If the method successfully updates an item and on_update param is defined,
|
||||
# the on_update parameter will be called as a method pasing in this object and the json from the response
|
||||
# This will return one of three things:
|
||||
# This will return one of two things:
|
||||
# 1. None if the existing_item does not need to be updated
|
||||
# 2. The response from Tower from patching to the endpoint. It's up to you to process the response and exit from the module.
|
||||
# 3. An ItemNotDefined exception, if the existing_item does not exist
|
||||
# Note: common error codes from the Tower API can cause the module to fail
|
||||
response = None
|
||||
if existing_item:
|
||||
@ -777,25 +574,6 @@ class TowerModule(AnsibleModule):
|
||||
# Sanity check: Did the server send back some kind of internal error?
|
||||
self.warn('Failed to release tower token {0}: {1}'.format(self.oauth_token_id, e))
|
||||
|
||||
def fail_json(self, **kwargs):
|
||||
# Try to log out if we are authenticated
|
||||
self.logout()
|
||||
if self.error_callback:
|
||||
self.error_callback(**kwargs)
|
||||
else:
|
||||
super(TowerModule, self).fail_json(**kwargs)
|
||||
|
||||
def exit_json(self, **kwargs):
|
||||
# Try to log out if we are authenticated
|
||||
self.logout()
|
||||
super(TowerModule, self).exit_json(**kwargs)
|
||||
|
||||
def warn(self, warning):
|
||||
if self.warn_callback is not None:
|
||||
self.warn_callback(warning)
|
||||
else:
|
||||
super(TowerModule, self).warn(warning)
|
||||
|
||||
def is_job_done(self, job_status):
|
||||
if job_status in ['new', 'pending', 'waiting', 'running']:
|
||||
return False
|
||||
|
50
awx_collection/plugins/module_utils/tower_awxkit.py
Normal file
50
awx_collection/plugins/module_utils/tower_awxkit.py
Normal file
@ -0,0 +1,50 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from . tower_module import TowerModule
|
||||
from ansible.module_utils.basic import missing_required_lib
|
||||
|
||||
try:
|
||||
from awxkit.api.client import Connection
|
||||
from awxkit.api.pages.api import ApiV2
|
||||
from awxkit.api import get_registered_page
|
||||
HAS_AWX_KIT = True
|
||||
except ImportError:
|
||||
HAS_AWX_KIT = False
|
||||
|
||||
|
||||
class TowerAWXKitModule(TowerModule):
|
||||
connection = None
|
||||
apiV2Ref = None
|
||||
|
||||
def __init__(self, argument_spec, **kwargs):
|
||||
kwargs['supports_check_mode'] = False
|
||||
|
||||
super(TowerAWXKitModule, self).__init__(argument_spec=argument_spec, **kwargs)
|
||||
|
||||
# Die if we don't have AWX_KIT installed
|
||||
if not HAS_AWX_KIT:
|
||||
self.exit_module(msg=missing_required_lib('awxkit'))
|
||||
|
||||
# Establish our conneciton object
|
||||
self.connection = Connection(self.host, verify=self.verify_ssl)
|
||||
|
||||
def authenticate(self):
|
||||
try:
|
||||
self.connection.login(username=self.username, password=self.password, token=self.oauth_token)
|
||||
# If we have neither of these, then we can try un-authenticated access
|
||||
self.authenticated = True
|
||||
except Exception:
|
||||
self.exit_module("Failed to authenticate")
|
||||
|
||||
def get_api_v2_object(self):
|
||||
if not self.apiV2Ref:
|
||||
if not self.authenticated:
|
||||
self.authenticate()
|
||||
v2_index = get_registered_page('/api/v2/')(self.connection).get()
|
||||
self.api_ref = ApiV2(connection=self.connection, **{'json': v2_index})
|
||||
return self.api_ref
|
||||
|
||||
def logout(self):
|
||||
if self.authenticated:
|
||||
self.connection.logout()
|
@ -91,7 +91,7 @@ def tower_check_mode(module):
|
||||
module.fail_json(changed=False, msg='Failed check mode: {0}'.format(excinfo))
|
||||
|
||||
|
||||
class TowerModule(AnsibleModule):
|
||||
class TowerLegacyModule(AnsibleModule):
|
||||
def __init__(self, argument_spec, **kwargs):
|
||||
args = dict(
|
||||
tower_host=dict(),
|
||||
@ -110,7 +110,7 @@ class TowerModule(AnsibleModule):
|
||||
('tower_config_file', 'validate_certs'),
|
||||
))
|
||||
|
||||
super(TowerModule, self).__init__(argument_spec=args, **kwargs)
|
||||
super(TowerLegacyModule, self).__init__(argument_spec=args, **kwargs)
|
||||
|
||||
if not HAS_TOWER_CLI:
|
||||
self.fail_json(msg=missing_required_lib('ansible-tower-cli'),
|
240
awx_collection/plugins/module_utils/tower_module.py
Normal file
240
awx_collection/plugins/module_utils/tower_module.py
Normal file
@ -0,0 +1,240 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
||||
from ansible.module_utils.six import PY2, string_types
|
||||
from ansible.module_utils.six.moves import StringIO
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlparse
|
||||
from ansible.module_utils.six.moves.configparser import ConfigParser, NoOptionError
|
||||
from socket import gethostbyname
|
||||
import re
|
||||
from os.path import isfile, expanduser, split, join, exists, isdir
|
||||
from os import access, R_OK, getcwd
|
||||
from distutils.util import strtobool
|
||||
|
||||
try:
|
||||
import yaml
|
||||
HAS_YAML = True
|
||||
except ImportError:
|
||||
HAS_YAML = False
|
||||
|
||||
|
||||
class ConfigFileException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ItemNotDefined(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class TowerModule(AnsibleModule):
|
||||
url = None
|
||||
AUTH_ARGSPEC = dict(
|
||||
tower_host=dict(required=False, fallback=(env_fallback, ['TOWER_HOST'])),
|
||||
tower_username=dict(required=False, fallback=(env_fallback, ['TOWER_USERNAME'])),
|
||||
tower_password=dict(no_log=True, required=False, fallback=(env_fallback, ['TOWER_PASSWORD'])),
|
||||
validate_certs=dict(type='bool', aliases=['tower_verify_ssl'], required=False, fallback=(env_fallback, ['TOWER_VERIFY_SSL'])),
|
||||
tower_oauthtoken=dict(type='raw', no_log=True, required=False, fallback=(env_fallback, ['TOWER_OAUTH_TOKEN'])),
|
||||
tower_config_file=dict(type='path', required=False, default=None),
|
||||
)
|
||||
short_params = {
|
||||
'host': 'tower_host',
|
||||
'username': 'tower_username',
|
||||
'password': 'tower_password',
|
||||
'verify_ssl': 'validate_certs',
|
||||
'oauth_token': 'tower_oauthtoken',
|
||||
}
|
||||
host = '127.0.0.1'
|
||||
username = None
|
||||
password = None
|
||||
verify_ssl = True
|
||||
oauth_token = None
|
||||
oauth_token_id = None
|
||||
authenticated = False
|
||||
config_name = 'tower_cli.cfg'
|
||||
ENCRYPTED_STRING = "$encrypted$"
|
||||
version_checked = False
|
||||
error_callback = None
|
||||
warn_callback = None
|
||||
|
||||
def __init__(self, argument_spec=None, direct_params=None, error_callback=None, warn_callback=None, **kwargs):
|
||||
full_argspec = {}
|
||||
full_argspec.update(TowerModule.AUTH_ARGSPEC)
|
||||
full_argspec.update(argument_spec)
|
||||
kwargs['supports_check_mode'] = True
|
||||
|
||||
self.error_callback = error_callback
|
||||
self.warn_callback = warn_callback
|
||||
|
||||
self.json_output = {'changed': False}
|
||||
|
||||
if direct_params is not None:
|
||||
self.params = direct_params
|
||||
else:
|
||||
super(TowerModule, self).__init__(argument_spec=full_argspec, **kwargs)
|
||||
|
||||
self.load_config_files()
|
||||
|
||||
# Parameters specified on command line will override settings in any config
|
||||
for short_param, long_param in self.short_params.items():
|
||||
direct_value = self.params.get(long_param)
|
||||
if direct_value is not None:
|
||||
setattr(self, short_param, direct_value)
|
||||
|
||||
# Perform magic depending on whether tower_oauthtoken is a string or a dict
|
||||
if self.params.get('tower_oauthtoken'):
|
||||
token_param = self.params.get('tower_oauthtoken')
|
||||
if type(token_param) is dict:
|
||||
if 'token' in token_param:
|
||||
self.oauth_token = self.params.get('tower_oauthtoken')['token']
|
||||
else:
|
||||
self.fail_json(msg="The provided dict in tower_oauthtoken did not properly contain the token entry")
|
||||
elif isinstance(token_param, string_types):
|
||||
self.oauth_token = self.params.get('tower_oauthtoken')
|
||||
else:
|
||||
error_msg = "The provided tower_oauthtoken type was not valid ({0}). Valid options are str or dict.".format(type(token_param).__name__)
|
||||
self.fail_json(msg=error_msg)
|
||||
|
||||
# Perform some basic validation
|
||||
if not re.match('^https{0,1}://', self.host):
|
||||
self.host = "https://{0}".format(self.host)
|
||||
|
||||
# Try to parse the hostname as a url
|
||||
try:
|
||||
self.url = urlparse(self.host)
|
||||
except Exception as e:
|
||||
self.fail_json(msg="Unable to parse tower_host as a URL ({1}): {0}".format(self.host, e))
|
||||
|
||||
# Try to resolve the hostname
|
||||
hostname = self.url.netloc.split(':')[0]
|
||||
try:
|
||||
gethostbyname(hostname)
|
||||
except Exception as e:
|
||||
self.fail_json(msg="Unable to resolve tower_host ({1}): {0}".format(hostname, e))
|
||||
|
||||
def load_config_files(self):
|
||||
# Load configs like TowerCLI would have from least import to most
|
||||
config_files = ['/etc/tower/tower_cli.cfg', join(expanduser("~"), ".{0}".format(self.config_name))]
|
||||
local_dir = getcwd()
|
||||
config_files.append(join(local_dir, self.config_name))
|
||||
while split(local_dir)[1]:
|
||||
local_dir = split(local_dir)[0]
|
||||
config_files.insert(2, join(local_dir, ".{0}".format(self.config_name)))
|
||||
|
||||
# If we have a specified tower config, load it
|
||||
if self.params.get('tower_config_file'):
|
||||
duplicated_params = [
|
||||
fn for fn in self.AUTH_ARGSPEC
|
||||
if fn != 'tower_config_file' and self.params.get(fn) is not None
|
||||
]
|
||||
if duplicated_params:
|
||||
self.warn((
|
||||
'The parameter(s) {0} were provided at the same time as tower_config_file. '
|
||||
'Precedence may be unstable, we suggest either using config file or params.'
|
||||
).format(', '.join(duplicated_params)))
|
||||
try:
|
||||
# TODO: warn if there are conflicts with other params
|
||||
self.load_config(self.params.get('tower_config_file'))
|
||||
except ConfigFileException as cfe:
|
||||
# Since we were told specifically to load this we want it to fail if we have an error
|
||||
self.fail_json(msg=cfe)
|
||||
else:
|
||||
for config_file in config_files:
|
||||
if exists(config_file) and not isdir(config_file):
|
||||
# Only throw a formatting error if the file exists and is not a directory
|
||||
try:
|
||||
self.load_config(config_file)
|
||||
except ConfigFileException:
|
||||
self.fail_json(msg='The config file {0} is not properly formatted'.format(config_file))
|
||||
|
||||
def load_config(self, config_path):
|
||||
# Validate the config file is an actual file
|
||||
if not isfile(config_path):
|
||||
raise ConfigFileException('The specified config file does not exist')
|
||||
|
||||
if not access(config_path, R_OK):
|
||||
raise ConfigFileException("The specified config file cannot be read")
|
||||
|
||||
# Read in the file contents:
|
||||
with open(config_path, 'r') as f:
|
||||
config_string = f.read()
|
||||
|
||||
# First try to yaml load the content (which will also load json)
|
||||
try:
|
||||
try_config_parsing = True
|
||||
if HAS_YAML:
|
||||
try:
|
||||
config_data = yaml.load(config_string, Loader=yaml.SafeLoader)
|
||||
# If this is an actual ini file, yaml will return the whole thing as a string instead of a dict
|
||||
if type(config_data) is not dict:
|
||||
raise AssertionError("The yaml config file is not properly formatted as a dict.")
|
||||
try_config_parsing = False
|
||||
|
||||
except(AttributeError, yaml.YAMLError, AssertionError):
|
||||
try_config_parsing = True
|
||||
|
||||
if try_config_parsing:
|
||||
# TowerCLI used to support a config file with a missing [general] section by prepending it if missing
|
||||
if '[general]' not in config_string:
|
||||
config_string = '[general]\n{0}'.format(config_string)
|
||||
|
||||
config = ConfigParser()
|
||||
|
||||
try:
|
||||
placeholder_file = StringIO(config_string)
|
||||
# py2 ConfigParser has readfp, that has been deprecated in favor of read_file in py3
|
||||
# This "if" removes the deprecation warning
|
||||
if hasattr(config, 'read_file'):
|
||||
config.read_file(placeholder_file)
|
||||
else:
|
||||
config.readfp(placeholder_file)
|
||||
|
||||
# If we made it here then we have values from reading the ini file, so let's pull them out into a dict
|
||||
config_data = {}
|
||||
for honorred_setting in self.short_params:
|
||||
try:
|
||||
config_data[honorred_setting] = config.get('general', honorred_setting)
|
||||
except NoOptionError:
|
||||
pass
|
||||
|
||||
except Exception as e:
|
||||
raise ConfigFileException("An unknown exception occured trying to ini load config file: {0}".format(e))
|
||||
|
||||
except Exception as e:
|
||||
raise ConfigFileException("An unknown exception occured trying to load config file: {0}".format(e))
|
||||
|
||||
# If we made it here, we have a dict which has values in it from our config, any final settings logic can be performed here
|
||||
for honorred_setting in self.short_params:
|
||||
if honorred_setting in config_data:
|
||||
# Veriffy SSL must be a boolean
|
||||
if honorred_setting == 'verify_ssl':
|
||||
if type(config_data[honorred_setting]) is str:
|
||||
setattr(self, honorred_setting, strtobool(config_data[honorred_setting]))
|
||||
else:
|
||||
setattr(self, honorred_setting, bool(config_data[honorred_setting]))
|
||||
else:
|
||||
setattr(self, honorred_setting, config_data[honorred_setting])
|
||||
|
||||
|
||||
def logout(self):
|
||||
# This method is intended to be overridden
|
||||
pass
|
||||
|
||||
def fail_json(self, **kwargs):
|
||||
# Try to log out if we are authenticated
|
||||
self.logout()
|
||||
if self.error_callback:
|
||||
self.error_callback(**kwargs)
|
||||
else:
|
||||
super(TowerModule, self).fail_json(**kwargs)
|
||||
|
||||
def exit_json(self, **kwargs):
|
||||
# Try to log out if we are authenticated
|
||||
self.logout()
|
||||
super(TowerModule, self).exit_json(**kwargs)
|
||||
|
||||
def warn(self, warning):
|
||||
if self.warn_callback is not None:
|
||||
self.warn_callback(warning)
|
||||
else:
|
||||
super(TowerModule, self).warn(warning)
|
@ -269,7 +269,7 @@ EXAMPLES = '''
|
||||
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
KIND_CHOICES = {
|
||||
'ssh': 'Machine',
|
||||
@ -336,7 +336,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec, required_one_of=[['kind', 'credential_type']])
|
||||
module = TowerAPIModule(argument_spec=argument_spec, required_one_of=[['kind', 'credential_type']])
|
||||
|
||||
# Extract our parameters
|
||||
name = module.params.get('name')
|
||||
|
@ -70,7 +70,7 @@ EXAMPLES = '''
|
||||
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def main():
|
||||
@ -85,7 +85,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
description = module.params.get('description')
|
||||
|
@ -81,7 +81,7 @@ EXAMPLES = '''
|
||||
RETURN = ''' # '''
|
||||
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
KIND_CHOICES = {
|
||||
'ssh': 'Machine',
|
||||
@ -105,7 +105,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
name = module.params.get('name')
|
||||
|
155
awx_collection/plugins/modules/tower_export.py
Normal file
155
awx_collection/plugins/modules/tower_export.py
Normal file
@ -0,0 +1,155 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8 -*-
|
||||
|
||||
# (c) 2017, John Westcott IV <john.westcott.iv@redhat.com>
|
||||
# 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: tower_export
|
||||
author: "John Westcott IV (@john-westcott-iv)"
|
||||
version_added: "3.7"
|
||||
short_description: export resources from Ansible Tower.
|
||||
description:
|
||||
- Export assets from Ansible Tower.
|
||||
options:
|
||||
all:
|
||||
description:
|
||||
- Export all assets
|
||||
type: bool
|
||||
default: 'False'
|
||||
organizations:
|
||||
description:
|
||||
- organization name to export
|
||||
default: ''
|
||||
type: str
|
||||
user:
|
||||
description:
|
||||
- user name to export
|
||||
default: ''
|
||||
type: str
|
||||
team:
|
||||
description:
|
||||
- team name to export
|
||||
default: ''
|
||||
type: str
|
||||
credential_type:
|
||||
description:
|
||||
- credential type name to export
|
||||
default: ''
|
||||
type: str
|
||||
credential:
|
||||
description:
|
||||
- credential name to export
|
||||
default: ''
|
||||
type: str
|
||||
notification_template:
|
||||
description:
|
||||
- notification template name to export
|
||||
default: ''
|
||||
type: str
|
||||
inventory_script:
|
||||
description:
|
||||
- inventory script name to export
|
||||
default: ''
|
||||
type: str
|
||||
inventory:
|
||||
description:
|
||||
- inventory name to export
|
||||
default: ''
|
||||
type: str
|
||||
project:
|
||||
description:
|
||||
- project name to export
|
||||
default: ''
|
||||
type: str
|
||||
job_template:
|
||||
description:
|
||||
- job template name to export
|
||||
default: ''
|
||||
type: str
|
||||
workflow:
|
||||
description:
|
||||
- workflow name to export
|
||||
default: ''
|
||||
type: str
|
||||
requirements:
|
||||
- "awxkit >= 9.3.0"
|
||||
notes:
|
||||
- Specifying a name of "all" for any asset type will export all items of that asset type.
|
||||
extends_documentation_fragment: awx.awx.auth
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Export all tower assets
|
||||
tower_export:
|
||||
all: True
|
||||
- name: Export all inventories
|
||||
tower_export:
|
||||
inventory: 'all'
|
||||
- name: Export a job template named "My Template" and all Credentials
|
||||
tower_export:
|
||||
job_template: "My Template"
|
||||
credential: 'all'
|
||||
'''
|
||||
|
||||
from os import environ
|
||||
|
||||
from ..module_utils.tower_awxkit import TowerAWXKitModule
|
||||
|
||||
try:
|
||||
from awxkit.api.pages.api import EXPORTABLE_RESOURCES
|
||||
HAS_EXPORTABLE_RESOURCES=True
|
||||
except ImportError:
|
||||
HAS_EXPORTABLE_RESOURCES=False
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = dict(
|
||||
all=dict(type='bool', default=False),
|
||||
)
|
||||
|
||||
# We are not going to raise an error here because the __init__ method of TowerAWXKitModule will do that for us
|
||||
if HAS_EXPORTABLE_RESOURCES:
|
||||
for resource in EXPORTABLE_RESOURCES:
|
||||
argument_spec[resource] = dict()
|
||||
|
||||
module = TowerAWXKitModule(argument_spec=argument_spec)
|
||||
|
||||
if not HAS_EXPORTABLE_RESOURCES:
|
||||
module.fail_json(msg="Your version of awxkit does not have import/export")
|
||||
|
||||
# The export process will never change a Tower system
|
||||
module.json_output['changed'] = False
|
||||
|
||||
# The exporter code currently works like the following:
|
||||
# Empty list == all assets of that type
|
||||
# string = just one asset of that type (by name)
|
||||
# None = skip asset type
|
||||
# Here we are going to setup a dict of values to export
|
||||
export_args = {}
|
||||
for resource in EXPORTABLE_RESOURCES:
|
||||
if module.params.get('all') or module.params.get(resource) == 'all':
|
||||
# If we are exporting everything or we got the keyword "all" we pass in an empty list for this asset type
|
||||
export_args[resource] = []
|
||||
else:
|
||||
# Otherwise we take either the string or None (if the parameter was not passed) to get one or no items
|
||||
export_args[resource] = module.params.get(resource)
|
||||
|
||||
# Run the export process
|
||||
module.json_output['assets'] = module.get_api_v2_object().export_assets(**export_args)
|
||||
|
||||
module.exit_json(**module.json_output)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -76,7 +76,7 @@ EXAMPLES = '''
|
||||
tower_config_file: "~/tower_cli.cfg"
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
import json
|
||||
|
||||
|
||||
@ -94,7 +94,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
name = module.params.get('name')
|
||||
|
@ -72,7 +72,7 @@ EXAMPLES = '''
|
||||
'''
|
||||
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
import json
|
||||
|
||||
|
||||
@ -89,7 +89,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
name = module.params.get('name')
|
||||
|
95
awx_collection/plugins/modules/tower_import.py
Normal file
95
awx_collection/plugins/modules/tower_import.py
Normal file
@ -0,0 +1,95 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8 -*-
|
||||
|
||||
# (c) 2017, John Westcott IV <john.westcott.iv@redhat.com>
|
||||
# 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: tower_import
|
||||
author: "John Westcott (@john-westcott-iv)"
|
||||
version_added: "3.7"
|
||||
short_description: import resources into Ansible Tower.
|
||||
description:
|
||||
- Import assets into Ansible Tower. See
|
||||
U(https://www.ansible.com/tower) for an overview.
|
||||
options:
|
||||
assets:
|
||||
description:
|
||||
- The assets to import.
|
||||
- This can be the output of tower_export or loaded from a file
|
||||
required: True
|
||||
type: dict
|
||||
requirements:
|
||||
- "awxkit >= 9.3.0"
|
||||
extends_documentation_fragment: awx.awx.auth
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Import all tower assets
|
||||
tower_import:
|
||||
assets: "{{ export_output.assets }}"
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_awxkit import TowerAWXKitModule
|
||||
|
||||
# These two lines are not needed if awxkit changes to do progamatic notifications on issues
|
||||
from ansible.module_utils.six.moves import StringIO
|
||||
import logging
|
||||
|
||||
# In this module we don't use EXPORTABLE_RESOURCES, we just want to validate that our installed awxkit has import/export
|
||||
try:
|
||||
from awxkit.api.pages.api import EXPORTABLE_RESOURCES
|
||||
HAS_EXPORTABLE_RESOURCES=True
|
||||
except ImportError:
|
||||
HAS_EXPORTABLE_RESOURCES=False
|
||||
|
||||
def main():
|
||||
argument_spec = dict(
|
||||
assets=dict(type='dict', required=True)
|
||||
)
|
||||
|
||||
module = TowerAWXKitModule(argument_spec=argument_spec, supports_check_mode=False)
|
||||
|
||||
assets = module.params.get('assets')
|
||||
|
||||
if not HAS_EXPORTABLE_RESOURCES:
|
||||
module.fail_json(msg="Your version of awxkit does not appear to have import/export")
|
||||
|
||||
# Currently the import process does not return anything on error
|
||||
# It simply just logs to pythons logger
|
||||
# Setup a log gobbler to get error messages from import_assets
|
||||
logger = logging.getLogger('awxkit.api.pages.api')
|
||||
logger.setLevel(logging.WARNING)
|
||||
log_capture_string = StringIO()
|
||||
ch = logging.StreamHandler(log_capture_string)
|
||||
ch.setLevel(logging.WARNING)
|
||||
logger.addHandler(ch)
|
||||
log_contents = ''
|
||||
|
||||
# Run the import process
|
||||
try:
|
||||
module.json_output['changed'] = module.get_api_v2_object().import_assets(assets)
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Failed to import assets {0}".format(e))
|
||||
finally:
|
||||
# Finally consume the logs incase there were any errors and die if there were
|
||||
log_contents = log_capture_string.getvalue()
|
||||
log_capture_string.close()
|
||||
if log_contents != '':
|
||||
module.fail_json(msg=log_contents)
|
||||
|
||||
module.exit_json(**module.json_output)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -71,7 +71,7 @@ EXAMPLES = '''
|
||||
'''
|
||||
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
import json
|
||||
|
||||
|
||||
@ -88,7 +88,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
name = module.params.get('name')
|
||||
|
@ -144,7 +144,7 @@ EXAMPLES = '''
|
||||
private: false
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
from json import dumps
|
||||
|
||||
|
||||
@ -184,7 +184,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
name = module.params.get('name')
|
||||
|
@ -50,7 +50,7 @@ id:
|
||||
'''
|
||||
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def main():
|
||||
@ -61,7 +61,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
job_id = module.params.get('job_id')
|
||||
|
@ -124,7 +124,7 @@ status:
|
||||
sample: pending
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def main():
|
||||
@ -146,7 +146,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
optional_args = {}
|
||||
# Extract our parameters
|
||||
|
@ -80,7 +80,7 @@ results:
|
||||
'''
|
||||
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def main():
|
||||
@ -93,7 +93,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(
|
||||
module = TowerAPIModule(
|
||||
argument_spec=argument_spec,
|
||||
mutually_exclusive=[
|
||||
('page', 'all_pages'),
|
||||
|
@ -317,7 +317,7 @@ EXAMPLES = '''
|
||||
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
import json
|
||||
|
||||
|
||||
@ -388,7 +388,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
name = module.params.get('name')
|
||||
|
@ -92,7 +92,7 @@ status:
|
||||
'''
|
||||
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
import time
|
||||
|
||||
|
||||
@ -120,7 +120,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
job_id = module.params.get('job_id')
|
||||
|
@ -54,7 +54,7 @@ EXAMPLES = '''
|
||||
organization: My Organization
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def main():
|
||||
@ -67,7 +67,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
name = module.params.get('name')
|
||||
|
@ -43,12 +43,12 @@ EXAMPLES = '''
|
||||
eula_accepted: True
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
module = TowerModule(
|
||||
module = TowerAPIModule(
|
||||
argument_spec=dict(
|
||||
data=dict(type='dict', required=True),
|
||||
eula_accepted=dict(type='bool', required=True),
|
||||
|
@ -62,11 +62,11 @@ EXAMPLES = '''
|
||||
'''
|
||||
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def main():
|
||||
module = TowerModule(argument_spec={})
|
||||
module = TowerAPIModule(argument_spec={})
|
||||
namespace = {
|
||||
'awx': 'awx',
|
||||
'tower': 'ansible'
|
||||
|
@ -300,7 +300,7 @@ EXAMPLES = '''
|
||||
RETURN = ''' # '''
|
||||
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
OLD_INPUT_NAMES = (
|
||||
'username', 'sender', 'recipients', 'use_tls',
|
||||
@ -355,7 +355,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
name = module.params.get('name')
|
||||
|
@ -88,7 +88,7 @@ EXAMPLES = '''
|
||||
tower_config_file: "~/tower_cli.cfg"
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def main():
|
||||
@ -106,7 +106,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
name = module.params.get('name')
|
||||
|
@ -157,7 +157,7 @@ EXAMPLES = '''
|
||||
|
||||
import time
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def wait_for_project_update(module, last_request):
|
||||
@ -205,7 +205,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
name = module.params.get('name')
|
||||
|
@ -134,7 +134,7 @@ assets:
|
||||
sample: [ {}, {} ]
|
||||
'''
|
||||
|
||||
from ..module_utils.ansible_tower import TowerModule, tower_auth_config, HAS_TOWER_CLI
|
||||
from ..module_utils.tower_legacy import TowerLegacyModule, tower_auth_config, HAS_TOWER_CLI
|
||||
|
||||
try:
|
||||
from tower_cli.cli.transfer.receive import Receiver
|
||||
@ -163,7 +163,7 @@ def main():
|
||||
workflow=dict(type='list', default=[], elements='str'),
|
||||
)
|
||||
|
||||
module = TowerModule(argument_spec=argument_spec, supports_check_mode=False)
|
||||
module = TowerLegacyModule(argument_spec=argument_spec, supports_check_mode=False)
|
||||
|
||||
module.deprecate(msg="This module is deprecated and will be replaced by the AWX CLI export command.", version="awx.awx:14.0.0")
|
||||
|
||||
|
@ -89,7 +89,7 @@ EXAMPLES = '''
|
||||
state: present
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def main():
|
||||
@ -109,7 +109,7 @@ def main():
|
||||
state=dict(choices=['present', 'absent'], default='present'),
|
||||
)
|
||||
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
role_type = module.params.pop('role')
|
||||
role_field = role_type + '_role'
|
||||
|
@ -136,7 +136,7 @@ EXAMPLES = '''
|
||||
register: result
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def main():
|
||||
@ -161,7 +161,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
rrule = module.params.get('rrule')
|
||||
|
@ -81,7 +81,7 @@ import os
|
||||
import sys
|
||||
|
||||
from ansible.module_utils.six.moves import StringIO
|
||||
from ..module_utils.ansible_tower import TowerModule, tower_auth_config, HAS_TOWER_CLI
|
||||
from ..module_utils.tower_legacy import TowerLegacyModule, tower_auth_config, HAS_TOWER_CLI
|
||||
|
||||
from tempfile import mkstemp
|
||||
|
||||
@ -103,7 +103,7 @@ def main():
|
||||
password_management=dict(default='default', choices=['default', 'random']),
|
||||
)
|
||||
|
||||
module = TowerModule(argument_spec=argument_spec, supports_check_mode=False)
|
||||
module = TowerLegacyModule(argument_spec=argument_spec, supports_check_mode=False)
|
||||
|
||||
module.deprecate(msg="This module is deprecated and will be replaced by the AWX CLI import command", version="awx.awx:14.0.0")
|
||||
|
||||
|
@ -70,7 +70,7 @@ EXAMPLES = '''
|
||||
last_name: "surname"
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
try:
|
||||
import yaml
|
||||
@ -111,7 +111,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(
|
||||
module = TowerAPIModule(
|
||||
argument_spec=argument_spec,
|
||||
required_one_of=[['name', 'settings']],
|
||||
mutually_exclusive=[['name', 'settings']],
|
||||
|
@ -60,7 +60,7 @@ EXAMPLES = '''
|
||||
tower_config_file: "~/tower_cli.cfg"
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def main():
|
||||
@ -74,7 +74,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
name = module.params.get('name')
|
||||
|
@ -117,7 +117,7 @@ tower_token:
|
||||
returned: on successful create
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def return_token(module, last_response):
|
||||
@ -143,7 +143,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(
|
||||
module = TowerAPIModule(
|
||||
argument_spec=argument_spec,
|
||||
mutually_exclusive=[
|
||||
('existing_token', 'existing_token_id'),
|
||||
|
@ -102,7 +102,7 @@ EXAMPLES = '''
|
||||
tower_config_file: "~/tower_cli.cfg"
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def main():
|
||||
@ -119,7 +119,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
username = module.params.get('username')
|
||||
|
@ -137,7 +137,7 @@ EXAMPLES = '''
|
||||
organization: Default
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
import json
|
||||
|
||||
@ -176,7 +176,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
name = module.params.get('name')
|
||||
|
@ -157,7 +157,7 @@ EXAMPLES = '''
|
||||
- my-first-node
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def main():
|
||||
@ -185,7 +185,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
identifier = module.params.get('identifier')
|
||||
|
@ -91,7 +91,7 @@ EXAMPLES = '''
|
||||
wait: False
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
import json
|
||||
import time
|
||||
|
||||
@ -111,7 +111,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
optional_args = {}
|
||||
# Extract our parameters
|
||||
|
@ -108,8 +108,8 @@ EXAMPLES = '''
|
||||
RETURN = ''' # '''
|
||||
|
||||
|
||||
from ..module_utils.ansible_tower import (
|
||||
TowerModule,
|
||||
from ..module_utils.tower_legacy import (
|
||||
TowerLegacyModule,
|
||||
tower_auth_config,
|
||||
tower_check_mode
|
||||
)
|
||||
@ -140,7 +140,7 @@ def main():
|
||||
state=dict(choices=['present', 'absent'], default='present'),
|
||||
)
|
||||
|
||||
module = TowerModule(
|
||||
module = TowerLegacyModule(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=False
|
||||
)
|
||||
|
@ -10,7 +10,7 @@ from contextlib import redirect_stdout, suppress
|
||||
from unittest import mock
|
||||
import logging
|
||||
|
||||
from requests.models import Response
|
||||
from requests.models import Response, PreparedRequest
|
||||
|
||||
import pytest
|
||||
|
||||
@ -23,6 +23,11 @@ try:
|
||||
except ImportError:
|
||||
HAS_TOWER_CLI = False
|
||||
|
||||
try:
|
||||
import awxkit
|
||||
HAS_AWX_KIT = True
|
||||
except ImportError:
|
||||
HAS_AWX_KIT = False
|
||||
|
||||
logger = logging.getLogger('awx.main.tests')
|
||||
|
||||
@ -90,7 +95,8 @@ def run_module(request, collection_import):
|
||||
if 'params' in kwargs and method == 'GET':
|
||||
# query params for GET are handled a bit differently by
|
||||
# tower-cli and python requests as opposed to REST framework APIRequestFactory
|
||||
kwargs_copy.setdefault('data', {})
|
||||
if not kwargs_copy.get('data'):
|
||||
kwargs_copy['data'] = {}
|
||||
if isinstance(kwargs['params'], dict):
|
||||
kwargs_copy['data'].update(kwargs['params'])
|
||||
elif isinstance(kwargs['params'], list):
|
||||
@ -117,6 +123,8 @@ def run_module(request, collection_import):
|
||||
request_user.username, resp.status_code
|
||||
)
|
||||
|
||||
resp.request = PreparedRequest()
|
||||
resp.request.prepare(method=method, url=url)
|
||||
return resp
|
||||
|
||||
def new_open(self, method, url, **kwargs):
|
||||
@ -142,11 +150,22 @@ def run_module(request, collection_import):
|
||||
def mock_load_params(self):
|
||||
self.params = module_params
|
||||
|
||||
with mock.patch.object(resource_module.TowerModule, '_load_params', new=mock_load_params):
|
||||
if getattr(resource_module, 'TowerAWXKitModule', None):
|
||||
resource_class = resource_module.TowerAWXKitModule
|
||||
elif getattr(resource_module, 'TowerAPIModule', None):
|
||||
resource_class = resource_module.TowerAPIModule
|
||||
elif getattr(resource_module, 'TowerLegacyModule', None):
|
||||
resource_class = resource_module.TowerLegacyModule
|
||||
else:
|
||||
raise("The module has neither a TowerLegacyModule, TowerAWXKitModule or a TowerAPIModule")
|
||||
|
||||
with mock.patch.object(resource_class, '_load_params', new=mock_load_params):
|
||||
# Call the test utility (like a mock server) instead of issuing HTTP requests
|
||||
with mock.patch('ansible.module_utils.urls.Request.open', new=new_open):
|
||||
if HAS_TOWER_CLI:
|
||||
tower_cli_mgr = mock.patch('tower_cli.api.Session.request', new=new_request)
|
||||
elif HAS_AWX_KIT:
|
||||
tower_cli_mgr = mock.patch('awxkit.api.client.requests.Session.request', new=new_request)
|
||||
else:
|
||||
tower_cli_mgr = suppress()
|
||||
with tower_cli_mgr:
|
||||
|
@ -30,12 +30,12 @@ def mock_ping_response(self, method, url, **kwargs):
|
||||
|
||||
|
||||
def test_version_warning(collection_import, silence_warning):
|
||||
TowerModule = collection_import('plugins.module_utils.tower_api').TowerModule
|
||||
TowerAPIModule = collection_import('plugins.module_utils.tower_api').TowerAPIModule
|
||||
cli_data = {'ANSIBLE_MODULE_ARGS': {}}
|
||||
testargs = ['module_file2.py', json.dumps(cli_data)]
|
||||
with mock.patch.object(sys, 'argv', testargs):
|
||||
with mock.patch('ansible.module_utils.urls.Request.open', new=mock_ping_response):
|
||||
my_module = TowerModule(argument_spec=dict())
|
||||
my_module = TowerAPIModule(argument_spec=dict())
|
||||
my_module._COLLECTION_VERSION = "1.0.0"
|
||||
my_module._COLLECTION_TYPE = "not-junk"
|
||||
my_module.collection_to_version['not-junk'] = 'not-junk'
|
||||
@ -46,12 +46,12 @@ def test_version_warning(collection_import, silence_warning):
|
||||
|
||||
|
||||
def test_type_warning(collection_import, silence_warning):
|
||||
TowerModule = collection_import('plugins.module_utils.tower_api').TowerModule
|
||||
TowerAPIModule = collection_import('plugins.module_utils.tower_api').TowerAPIModule
|
||||
cli_data = {'ANSIBLE_MODULE_ARGS': {}}
|
||||
testargs = ['module_file2.py', json.dumps(cli_data)]
|
||||
with mock.patch.object(sys, 'argv', testargs):
|
||||
with mock.patch('ansible.module_utils.urls.Request.open', new=mock_ping_response):
|
||||
my_module = TowerModule(argument_spec={})
|
||||
my_module = TowerAPIModule(argument_spec={})
|
||||
my_module._COLLECTION_VERSION = "1.2.3"
|
||||
my_module._COLLECTION_TYPE = "junk"
|
||||
my_module.collection_to_version['junk'] = 'junk'
|
||||
@ -63,7 +63,7 @@ def test_type_warning(collection_import, silence_warning):
|
||||
|
||||
def test_duplicate_config(collection_import, silence_warning):
|
||||
# imports done here because of PATH issues unique to this test suite
|
||||
TowerModule = collection_import('plugins.module_utils.tower_api').TowerModule
|
||||
TowerAPIModule = collection_import('plugins.module_utils.tower_api').TowerAPIModule
|
||||
data = {
|
||||
'name': 'zigzoom',
|
||||
'zig': 'zoom',
|
||||
@ -71,12 +71,12 @@ def test_duplicate_config(collection_import, silence_warning):
|
||||
'tower_config_file': 'my_config'
|
||||
}
|
||||
|
||||
with mock.patch.object(TowerModule, 'load_config') as mock_load:
|
||||
with mock.patch.object(TowerAPIModule, 'load_config') as mock_load:
|
||||
argument_spec = dict(
|
||||
name=dict(required=True),
|
||||
zig=dict(type='str'),
|
||||
)
|
||||
TowerModule(argument_spec=argument_spec, direct_params=data)
|
||||
TowerAPIModule(argument_spec=argument_spec, direct_params=data)
|
||||
assert mock_load.mock_calls[-1] == mock.call('my_config')
|
||||
|
||||
silence_warning.assert_called_once_with(
|
||||
@ -92,8 +92,8 @@ def test_no_templated_values(collection_import):
|
||||
Those replacements should happen at build time, so they should not be
|
||||
checked into source.
|
||||
"""
|
||||
TowerModule = collection_import('plugins.module_utils.tower_api').TowerModule
|
||||
assert TowerModule._COLLECTION_VERSION == "0.0.1-devel", (
|
||||
TowerAPIModule = collection_import('plugins.module_utils.tower_api').TowerAPIModule
|
||||
assert TowerAPIModule._COLLECTION_VERSION == "0.0.1-devel", (
|
||||
'The collection version is templated when the collection is built '
|
||||
'and the code should retain the placeholder of "0.0.1-devel".'
|
||||
)
|
||||
|
@ -96,7 +96,7 @@ EXAMPLES = '''
|
||||
{% endif %}
|
||||
'''
|
||||
|
||||
from ..module_utils.tower_api import TowerModule
|
||||
from ..module_utils.tower_api import TowerAPIModule
|
||||
|
||||
|
||||
def main():
|
||||
@ -142,7 +142,7 @@ def main():
|
||||
)
|
||||
|
||||
# Create a module for ourselves
|
||||
module = TowerModule(argument_spec=argument_spec)
|
||||
module = TowerAPIModule(argument_spec=argument_spec)
|
||||
|
||||
# Extract our parameters
|
||||
{% for option in item['json']['actions']['POST'] %}
|
||||
|
Loading…
Reference in New Issue
Block a user