mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 17:37:37 -02:30
Build-in inventory plugin code structure with gce working
supporting and related changes - Fix inconsistency between can_update / can_start - Avoid creating inventory file twice unnecessarily - Non-functional consolidation in Azure injection logic - Inject GCE creds as indented JSON for readability - Create new injector class structure, add gce - Reduce management command overrides of runtime environment
This commit is contained in:
@@ -124,7 +124,13 @@ class AnsibleInventoryLoader(object):
|
|||||||
|
|
||||||
def get_base_args(self):
|
def get_base_args(self):
|
||||||
# get ansible-inventory absolute path for running in bubblewrap/proot, in Popen
|
# get ansible-inventory absolute path for running in bubblewrap/proot, in Popen
|
||||||
bargs= [self.get_path_to_ansible_inventory(), '-i', self.source]
|
|
||||||
|
# NOTE: why do we add "python" to the start of these args?
|
||||||
|
# the script that runs ansible-inventory specifies a python interpreter
|
||||||
|
# that makes no sense in light of the fact that we put all the dependencies
|
||||||
|
# inside of /venv/ansible, so we override the specified interpreter
|
||||||
|
# https://github.com/ansible/ansible/issues/50714
|
||||||
|
bargs= ['python', self.get_path_to_ansible_inventory(), '-i', self.source]
|
||||||
logger.debug('Using base command: {}'.format(' '.join(bargs)))
|
logger.debug('Using base command: {}'.format(' '.join(bargs)))
|
||||||
return bargs
|
return bargs
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,15 @@ def gce(cred, env, private_data_dir):
|
|||||||
'type': 'service_account',
|
'type': 'service_account',
|
||||||
'private_key': cred.get_input('ssh_key_data', default=''),
|
'private_key': cred.get_input('ssh_key_data', default=''),
|
||||||
'client_email': username,
|
'client_email': username,
|
||||||
'project_id': project
|
'project_id': project,
|
||||||
|
# need token uri for inventory plugins
|
||||||
|
# should this really be hard coded? Good question.
|
||||||
|
'token_uri': 'https://accounts.google.com/o/oauth2/token',
|
||||||
}
|
}
|
||||||
handle, path = tempfile.mkstemp(dir=private_data_dir)
|
|
||||||
f = os.fdopen(handle, 'w')
|
path = os.path.join(private_data_dir, 'creds.json')
|
||||||
json.dump(json_cred, f)
|
with open(path, 'w') as f:
|
||||||
f.close()
|
json.dump(json_cred, f, indent=2)
|
||||||
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
|
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
|
||||||
env['GCE_CREDENTIALS_FILE_PATH'] = path
|
env['GCE_CREDENTIALS_FILE_PATH'] = path
|
||||||
|
|
||||||
@@ -38,13 +41,13 @@ def azure_rm(cred, env, private_data_dir):
|
|||||||
client = cred.get_input('client', default='')
|
client = cred.get_input('client', default='')
|
||||||
tenant = cred.get_input('tenant', default='')
|
tenant = cred.get_input('tenant', default='')
|
||||||
|
|
||||||
|
env['AZURE_SUBSCRIPTION_ID'] = cred.get_input('subscription', default='')
|
||||||
|
|
||||||
if len(client) and len(tenant):
|
if len(client) and len(tenant):
|
||||||
env['AZURE_CLIENT_ID'] = client
|
env['AZURE_CLIENT_ID'] = client
|
||||||
env['AZURE_TENANT'] = tenant
|
env['AZURE_TENANT'] = tenant
|
||||||
env['AZURE_SECRET'] = cred.get_input('secret', default='')
|
env['AZURE_SECRET'] = cred.get_input('secret', default='')
|
||||||
env['AZURE_SUBSCRIPTION_ID'] = cred.get_input('subscription', default='')
|
|
||||||
else:
|
else:
|
||||||
env['AZURE_SUBSCRIPTION_ID'] = cred.get_input('subscription', default='')
|
|
||||||
env['AZURE_AD_USER'] = cred.get_input('username', default='')
|
env['AZURE_AD_USER'] = cred.get_input('username', default='')
|
||||||
env['AZURE_PASSWORD'] = cred.get_input('password', default='')
|
env['AZURE_PASSWORD'] = cred.get_input('password', default='')
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ import re
|
|||||||
import copy
|
import copy
|
||||||
import os.path
|
import os.path
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
|
import yaml
|
||||||
|
import configparser
|
||||||
|
import stat
|
||||||
|
import tempfile
|
||||||
|
from distutils.version import LooseVersion as Version
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -1015,6 +1020,8 @@ class InventorySourceOptions(BaseModel):
|
|||||||
Common fields for InventorySource and InventoryUpdate.
|
Common fields for InventorySource and InventoryUpdate.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
injectors = dict()
|
||||||
|
|
||||||
SOURCE_CHOICES = [
|
SOURCE_CHOICES = [
|
||||||
('', _('Manual')),
|
('', _('Manual')),
|
||||||
('file', _('File, Directory or Script')),
|
('file', _('File, Directory or Script')),
|
||||||
@@ -1308,6 +1315,8 @@ class InventorySourceOptions(BaseModel):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def get_inventory_plugin_name(self):
|
def get_inventory_plugin_name(self):
|
||||||
|
if self.source in InventorySourceOptions.injectors:
|
||||||
|
return InventorySourceOptions.injectors[self.source].plugin_name
|
||||||
if self.source in CLOUD_PROVIDERS or self.source == 'custom':
|
if self.source in CLOUD_PROVIDERS or self.source == 'custom':
|
||||||
# TODO: today, all vendored sources are scripts
|
# TODO: today, all vendored sources are scripts
|
||||||
# in future release inventory plugins will replace these
|
# in future release inventory plugins will replace these
|
||||||
@@ -1532,8 +1541,15 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions, RelatedJobsMix
|
|||||||
return bool(self.source_script)
|
return bool(self.source_script)
|
||||||
elif self.source == 'scm':
|
elif self.source == 'scm':
|
||||||
return bool(self.source_project)
|
return bool(self.source_project)
|
||||||
else:
|
elif self.source == 'file':
|
||||||
return bool(self.source in CLOUD_INVENTORY_SOURCES)
|
return False
|
||||||
|
elif self.source == 'ec2':
|
||||||
|
# Permit credential-less ec2 updates to allow IAM roles
|
||||||
|
return True
|
||||||
|
elif self.source == 'gce':
|
||||||
|
# These updates will hang if correct credential is not supplied
|
||||||
|
return bool(self.get_cloud_credential().kind == 'gce')
|
||||||
|
return True
|
||||||
|
|
||||||
def create_inventory_update(self, **kwargs):
|
def create_inventory_update(self, **kwargs):
|
||||||
return self.create_unified_job(**kwargs)
|
return self.create_unified_job(**kwargs)
|
||||||
@@ -1695,6 +1711,14 @@ class InventoryUpdate(UnifiedJob, InventorySourceOptions, JobNotificationMixin,
|
|||||||
def get_ui_url(self):
|
def get_ui_url(self):
|
||||||
return urljoin(settings.TOWER_URL_BASE, "/#/jobs/inventory/{}".format(self.pk))
|
return urljoin(settings.TOWER_URL_BASE, "/#/jobs/inventory/{}".format(self.pk))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ansible_virtualenv_path(self):
|
||||||
|
if self.inventory and self.inventory.organization:
|
||||||
|
virtualenv = self.inventory.organization.custom_virtualenv
|
||||||
|
if virtualenv:
|
||||||
|
return virtualenv
|
||||||
|
return settings.ANSIBLE_VENV_PATH
|
||||||
|
|
||||||
def get_actual_source_path(self):
|
def get_actual_source_path(self):
|
||||||
'''Alias to source_path that combines with project path for for SCM file based sources'''
|
'''Alias to source_path that combines with project path for for SCM file based sources'''
|
||||||
if self.inventory_source_id is None or self.inventory_source.source_project_id is None:
|
if self.inventory_source_id is None or self.inventory_source.source_project_id is None:
|
||||||
@@ -1717,13 +1741,7 @@ class InventoryUpdate(UnifiedJob, InventorySourceOptions, JobNotificationMixin,
|
|||||||
def can_start(self):
|
def can_start(self):
|
||||||
if not super(InventoryUpdate, self).can_start:
|
if not super(InventoryUpdate, self).can_start:
|
||||||
return False
|
return False
|
||||||
|
elif not self.inventory_source or not self.inventory_source._can_update():
|
||||||
if (self.source not in ('custom', 'ec2', 'scm') and
|
|
||||||
not (self.get_cloud_credential())):
|
|
||||||
return False
|
|
||||||
elif self.source == 'scm' and not self.inventory_source.source_project:
|
|
||||||
return False
|
|
||||||
elif self.source == 'file':
|
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -1801,3 +1819,88 @@ class CustomInventoryScript(CommonModelNameNotUnique, ResourceMixin):
|
|||||||
|
|
||||||
def get_absolute_url(self, request=None):
|
def get_absolute_url(self, request=None):
|
||||||
return reverse('api:inventory_script_detail', kwargs={'pk': self.pk}, request=request)
|
return reverse('api:inventory_script_detail', kwargs={'pk': self.pk}, request=request)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: move these to their own file somewhere?
|
||||||
|
class PluginFileInjector(object):
|
||||||
|
plugin_name = None
|
||||||
|
initial_version = None
|
||||||
|
|
||||||
|
def __init__(self, ansible_version):
|
||||||
|
# This is InventoryOptions instance, could be source or inventory update
|
||||||
|
self.ansible_version = ansible_version
|
||||||
|
|
||||||
|
@property
|
||||||
|
def filename(self):
|
||||||
|
return '{0}.yml'.format(self.plugin_name)
|
||||||
|
|
||||||
|
def inventory_contents(self, inventory_source):
|
||||||
|
return yaml.safe_dump(self.inventory_as_dict(inventory_source), default_flow_style=False)
|
||||||
|
|
||||||
|
def should_use_plugin(self):
|
||||||
|
return bool(
|
||||||
|
self.initial_version and
|
||||||
|
Version(self.ansible_version) >= Version(self.initial_version)
|
||||||
|
)
|
||||||
|
|
||||||
|
def build_env(self, *args, **kwargs):
|
||||||
|
if self.should_use_plugin():
|
||||||
|
return self.build_plugin_env(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
return self.build_script_env(*args, **kwargs)
|
||||||
|
|
||||||
|
def build_plugin_env(self, inventory_update, env, private_data_dir):
|
||||||
|
return env
|
||||||
|
|
||||||
|
def build_script_env(self, inventory_update, env, private_data_dir):
|
||||||
|
return env
|
||||||
|
|
||||||
|
def build_private_data(self, *args, **kwargs):
|
||||||
|
if self.should_use_plugin():
|
||||||
|
return self.build_private_data(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
return self.build_private_data(*args, **kwargs)
|
||||||
|
|
||||||
|
def build_script_private_data(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def build_plugin_private_data(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class gce(PluginFileInjector):
|
||||||
|
plugin_name = 'gcp_compute'
|
||||||
|
initial_version = '2.6'
|
||||||
|
|
||||||
|
def build_script_env(self, inventory_update, env, private_data_dir):
|
||||||
|
env['GCE_ZONE'] = inventory_update.source_regions if inventory_update.source_regions != 'all' else '' # noqa
|
||||||
|
|
||||||
|
# by default, the GCE inventory source caches results on disk for
|
||||||
|
# 5 minutes; disable this behavior
|
||||||
|
cp = configparser.ConfigParser()
|
||||||
|
cp.add_section('cache')
|
||||||
|
cp.set('cache', 'cache_max_age', '0')
|
||||||
|
handle, path = tempfile.mkstemp(dir=private_data_dir)
|
||||||
|
cp.write(os.fdopen(handle, 'w'))
|
||||||
|
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
|
||||||
|
env['GCE_INI_PATH'] = path
|
||||||
|
return env
|
||||||
|
|
||||||
|
def inventory_as_dict(self, inventory_source):
|
||||||
|
# NOTE: generalizing this to be use templating like credential types would be nice
|
||||||
|
# but with YAML content that need to inject list parameters into the YAML,
|
||||||
|
# it is hard to see any clean way we can possibly do this
|
||||||
|
ret = dict(
|
||||||
|
plugin='gcp_compute',
|
||||||
|
projects=[inventory_source.get_cloud_credential().project],
|
||||||
|
filters=None, # necessary cruft, see: https://github.com/ansible/ansible/pull/50025
|
||||||
|
service_account_file="creds.json",
|
||||||
|
auth_kind="serviceaccount"
|
||||||
|
)
|
||||||
|
if inventory_source.source_regions:
|
||||||
|
ret['zones'] = inventory_source.source_regions.split(',')
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
for cls in PluginFileInjector.__subclasses__():
|
||||||
|
InventorySourceOptions.injectors[cls.__name__] = cls
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ from awx.main.access import access_registry
|
|||||||
from awx.main.models import (
|
from awx.main.models import (
|
||||||
Schedule, TowerScheduleState, Instance, InstanceGroup,
|
Schedule, TowerScheduleState, Instance, InstanceGroup,
|
||||||
UnifiedJob, Notification,
|
UnifiedJob, Notification,
|
||||||
Inventory, SmartInventoryMembership,
|
Inventory, InventorySource, SmartInventoryMembership,
|
||||||
Job, AdHocCommand, ProjectUpdate, InventoryUpdate, SystemJob,
|
Job, AdHocCommand, ProjectUpdate, InventoryUpdate, SystemJob,
|
||||||
JobEvent, ProjectUpdateEvent, InventoryUpdateEvent, AdHocCommandEvent, SystemJobEvent,
|
JobEvent, ProjectUpdateEvent, InventoryUpdateEvent, AdHocCommandEvent, SystemJobEvent,
|
||||||
build_safe_env
|
build_safe_env
|
||||||
@@ -67,6 +67,7 @@ from awx.main.utils import (get_ssh_version, update_scm_url,
|
|||||||
get_licenser,
|
get_licenser,
|
||||||
ignore_inventory_computed_fields,
|
ignore_inventory_computed_fields,
|
||||||
ignore_inventory_group_removal, extract_ansible_vars, schedule_task_manager)
|
ignore_inventory_group_removal, extract_ansible_vars, schedule_task_manager)
|
||||||
|
from awx.main.utils.common import _get_ansible_version
|
||||||
from awx.main.utils.safe_yaml import safe_dump, sanitize_jinja
|
from awx.main.utils.safe_yaml import safe_dump, sanitize_jinja
|
||||||
from awx.main.utils.reload import stop_local_services
|
from awx.main.utils.reload import stop_local_services
|
||||||
from awx.main.utils.pglock import advisory_lock
|
from awx.main.utils.pglock import advisory_lock
|
||||||
@@ -713,12 +714,25 @@ class BaseTask(object):
|
|||||||
logger.error('Failed to update %s after %d retries.',
|
logger.error('Failed to update %s after %d retries.',
|
||||||
self.model._meta.object_name, _attempt)
|
self.model._meta.object_name, _attempt)
|
||||||
|
|
||||||
|
def get_ansible_version(self, instance):
|
||||||
|
if not hasattr(self, '_ansible_version'):
|
||||||
|
self._ansible_version = _get_ansible_version(
|
||||||
|
ansible_path=self.get_path_to_ansible(instance, executable='ansible'))
|
||||||
|
return self._ansible_version
|
||||||
|
|
||||||
def get_path_to(self, *args):
|
def get_path_to(self, *args):
|
||||||
'''
|
'''
|
||||||
Return absolute path relative to this file.
|
Return absolute path relative to this file.
|
||||||
'''
|
'''
|
||||||
return os.path.abspath(os.path.join(os.path.dirname(__file__), *args))
|
return os.path.abspath(os.path.join(os.path.dirname(__file__), *args))
|
||||||
|
|
||||||
|
def get_path_to_ansible(self, instance, executable='ansible-playbook', **kwargs):
|
||||||
|
venv_path = getattr(instance, 'ansible_virtualenv_path', settings.ANSIBLE_VENV_PATH)
|
||||||
|
venv_exe = os.path.join(venv_path, 'bin', executable)
|
||||||
|
if os.path.exists(venv_exe):
|
||||||
|
return venv_exe
|
||||||
|
return shutil.which(executable)
|
||||||
|
|
||||||
def build_private_data(self, instance, private_data_dir):
|
def build_private_data(self, instance, private_data_dir):
|
||||||
'''
|
'''
|
||||||
Return SSH private key data (only if stored in DB as ssh_key_data).
|
Return SSH private key data (only if stored in DB as ssh_key_data).
|
||||||
@@ -2134,9 +2148,13 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
def build_env(self, inventory_update, private_data_dir, isolated, private_data_files=None):
|
def build_env(self, inventory_update, private_data_dir, isolated, private_data_files=None):
|
||||||
"""Build environment dictionary for inventory import.
|
"""Build environment dictionary for inventory import.
|
||||||
|
|
||||||
This is the mechanism by which any data that needs to be passed
|
This used to be the mechanism by which any data that needs to be passed
|
||||||
to the inventory update script is set up. In particular, this is how
|
to the inventory update script is set up. In particular, this is how
|
||||||
inventory update is aware of its proper credentials.
|
inventory update is aware of its proper credentials.
|
||||||
|
|
||||||
|
Most environment injection is now accomplished by the credential
|
||||||
|
injectors. The primary purpose this still serves is to
|
||||||
|
still point to the inventory update INI or config file.
|
||||||
"""
|
"""
|
||||||
env = super(RunInventoryUpdate, self).build_env(inventory_update,
|
env = super(RunInventoryUpdate, self).build_env(inventory_update,
|
||||||
private_data_dir,
|
private_data_dir,
|
||||||
@@ -2145,6 +2163,7 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
if private_data_files is None:
|
if private_data_files is None:
|
||||||
private_data_files = {}
|
private_data_files = {}
|
||||||
self.add_awx_venv(env)
|
self.add_awx_venv(env)
|
||||||
|
self.add_ansible_venv(inventory_update.ansible_virtualenv_path, env)
|
||||||
# Pass inventory source ID to inventory script.
|
# Pass inventory source ID to inventory script.
|
||||||
env['INVENTORY_SOURCE_ID'] = str(inventory_update.inventory_source_id)
|
env['INVENTORY_SOURCE_ID'] = str(inventory_update.inventory_source_id)
|
||||||
env['INVENTORY_UPDATE_ID'] = str(inventory_update.pk)
|
env['INVENTORY_UPDATE_ID'] = str(inventory_update.pk)
|
||||||
@@ -2176,25 +2195,19 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
inventory_update.get_cloud_credential(), ''
|
inventory_update.get_cloud_credential(), ''
|
||||||
)
|
)
|
||||||
|
|
||||||
if inventory_update.source == 'gce':
|
if inventory_update.source in InventorySource.injectors:
|
||||||
env['GCE_ZONE'] = inventory_update.source_regions if inventory_update.source_regions != 'all' else '' # noqa
|
# TODO: mapping from credential.kind to inventory_source.source
|
||||||
|
injector = InventorySource.injectors[inventory_update.source](self.get_ansible_version(inventory_update))
|
||||||
|
env = injector.build_env(inventory_update, env, private_data_dir)
|
||||||
|
|
||||||
# by default, the GCE inventory source caches results on disk for
|
if inventory_update.source == 'tower':
|
||||||
# 5 minutes; disable this behavior
|
env['TOWER_INVENTORY'] = inventory_update.instance_filters
|
||||||
cp = configparser.ConfigParser()
|
env['TOWER_LICENSE_TYPE'] = get_licenser().validate()['license_type']
|
||||||
cp.add_section('cache')
|
|
||||||
cp.set('cache', 'cache_max_age', '0')
|
if inventory_update.source in ['scm', 'custom']:
|
||||||
handle, path = tempfile.mkstemp(dir=private_data_dir)
|
|
||||||
cp.write(os.fdopen(handle, 'w'))
|
|
||||||
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
|
|
||||||
env['GCE_INI_PATH'] = path
|
|
||||||
elif inventory_update.source in ['scm', 'custom']:
|
|
||||||
for env_k in inventory_update.source_vars_dict:
|
for env_k in inventory_update.source_vars_dict:
|
||||||
if str(env_k) not in env and str(env_k) not in settings.INV_ENV_VARIABLE_BLACKLIST:
|
if str(env_k) not in env and str(env_k) not in settings.INV_ENV_VARIABLE_BLACKLIST:
|
||||||
env[str(env_k)] = str(inventory_update.source_vars_dict[env_k])
|
env[str(env_k)] = str(inventory_update.source_vars_dict[env_k])
|
||||||
elif inventory_update.source == 'tower':
|
|
||||||
env['TOWER_INVENTORY'] = inventory_update.instance_filters
|
|
||||||
env['TOWER_LICENSE_TYPE'] = get_licenser().validate()['license_type']
|
|
||||||
elif inventory_update.source == 'file':
|
elif inventory_update.source == 'file':
|
||||||
raise NotImplementedError('Cannot update file sources through the task system.')
|
raise NotImplementedError('Cannot update file sources through the task system.')
|
||||||
return env
|
return env
|
||||||
@@ -2259,33 +2272,61 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
getattr(settings, '%s_INSTANCE_ID_VAR' % src.upper()),])
|
getattr(settings, '%s_INSTANCE_ID_VAR' % src.upper()),])
|
||||||
# Add arguments for the source inventory script
|
# Add arguments for the source inventory script
|
||||||
args.append('--source')
|
args.append('--source')
|
||||||
if src in CLOUD_PROVIDERS:
|
args.append(self.build_inventory(inventory_update, private_data_dir))
|
||||||
# Get the path to the inventory plugin, and append it to our
|
if src == 'custom':
|
||||||
# arguments.
|
|
||||||
plugin_path = self.get_path_to('..', 'plugins', 'inventory',
|
|
||||||
'%s.py' % src)
|
|
||||||
args.append(plugin_path)
|
|
||||||
elif src == 'scm':
|
|
||||||
args.append(inventory_update.get_actual_source_path())
|
|
||||||
elif src == 'custom':
|
|
||||||
handle, path = tempfile.mkstemp(dir=private_data_dir)
|
|
||||||
f = os.fdopen(handle, 'w')
|
|
||||||
if inventory_update.source_script is None:
|
|
||||||
raise RuntimeError('Inventory Script does not exist')
|
|
||||||
f.write(inventory_update.source_script.script)
|
|
||||||
f.close()
|
|
||||||
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
|
||||||
args.append(path)
|
|
||||||
args.append("--custom")
|
args.append("--custom")
|
||||||
args.append('-v%d' % inventory_update.verbosity)
|
args.append('-v%d' % inventory_update.verbosity)
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
args.append('--traceback')
|
args.append('--traceback')
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
def build_inventory(self, inventory_update, private_data_dir):
|
||||||
|
src = inventory_update.source
|
||||||
|
if src in CLOUD_PROVIDERS:
|
||||||
|
if src in InventorySource.injectors:
|
||||||
|
cloud_cred = inventory_update.get_cloud_credential()
|
||||||
|
injector = InventorySource.injectors[cloud_cred.kind](self.get_ansible_version(inventory_update))
|
||||||
|
content = injector.inventory_contents(inventory_update)
|
||||||
|
content = content.encode('utf-8')
|
||||||
|
# must be a statically named file
|
||||||
|
inventory_path = os.path.join(private_data_dir, injector.filename)
|
||||||
|
with open(inventory_path, 'w') as f:
|
||||||
|
f.write(content)
|
||||||
|
os.chmod(inventory_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||||
|
else:
|
||||||
|
# Get the path to the inventory plugin, and append it to our
|
||||||
|
# arguments.
|
||||||
|
inventory_path = self.get_path_to('..', 'plugins', 'inventory', '%s.py' % src)
|
||||||
|
elif src == 'scm':
|
||||||
|
inventory_path = inventory_update.get_actual_source_path()
|
||||||
|
elif src == 'custom':
|
||||||
|
handle, inventory_path = tempfile.mkstemp(dir=private_data_dir)
|
||||||
|
f = os.fdopen(handle, 'w')
|
||||||
|
if inventory_update.source_script is None:
|
||||||
|
raise RuntimeError('Inventory Script does not exist')
|
||||||
|
f.write(inventory_update.source_script.script)
|
||||||
|
f.close()
|
||||||
|
os.chmod(inventory_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||||
|
return inventory_path
|
||||||
|
|
||||||
def build_cwd(self, inventory_update, private_data_dir):
|
def build_cwd(self, inventory_update, private_data_dir):
|
||||||
if inventory_update.source == 'scm' and inventory_update.source_project_update:
|
'''
|
||||||
|
There are two cases where the inventory "source" is in a different
|
||||||
|
location from the private data:
|
||||||
|
- deprecated vendored inventory scripts in awx/plugins/inventory
|
||||||
|
- SCM, where source needs to live in the project folder
|
||||||
|
in these cases, the inventory does not exist in the standard tempdir
|
||||||
|
'''
|
||||||
|
src = inventory_update.source
|
||||||
|
if src == 'scm' and inventory_update.source_project_update:
|
||||||
return inventory_update.source_project_update.get_project_path(check_if_exists=False)
|
return inventory_update.source_project_update.get_project_path(check_if_exists=False)
|
||||||
return self.get_path_to('..', 'plugins', 'inventory')
|
if src in CLOUD_PROVIDERS:
|
||||||
|
injector = None
|
||||||
|
if src in InventorySource.injectors:
|
||||||
|
injector = InventorySource.injectors[src](self.get_ansible_version(inventory_update))
|
||||||
|
if (not injector) or (not injector.should_use_plugin()):
|
||||||
|
return self.get_path_to('..', 'plugins', 'inventory')
|
||||||
|
return private_data_dir
|
||||||
|
|
||||||
def build_playbook_path_relative_to_cwd(self, inventory_update, private_data_dir):
|
def build_playbook_path_relative_to_cwd(self, inventory_update, private_data_dir):
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ azure-graphrbac==0.40.0
|
|||||||
# AWS
|
# AWS
|
||||||
boto==2.47.0 # last which does not break ec2 scripts
|
boto==2.47.0 # last which does not break ec2 scripts
|
||||||
boto3==1.6.2
|
boto3==1.6.2
|
||||||
|
google-auth==1.6.2 # needed for gce inventory imports
|
||||||
# netconf for network modules
|
# netconf for network modules
|
||||||
ncclient==0.6.3
|
ncclient==0.6.3
|
||||||
# netaddr filter
|
# netaddr filter
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ bcrypt==3.1.4 # via paramiko
|
|||||||
boto3==1.6.2
|
boto3==1.6.2
|
||||||
boto==2.47.0
|
boto==2.47.0
|
||||||
botocore==1.9.3 # via boto3, s3transfer
|
botocore==1.9.3 # via boto3, s3transfer
|
||||||
|
cachetools==3.0.0 # via google-auth
|
||||||
certifi==2018.1.18 # via msrest, requests
|
certifi==2018.1.18 # via msrest, requests
|
||||||
cffi==1.11.5 # via bcrypt, cryptography, pynacl
|
cffi==1.11.5 # via bcrypt, cryptography, pynacl
|
||||||
chardet==3.0.4 # via requests
|
chardet==3.0.4 # via requests
|
||||||
@@ -50,6 +51,8 @@ docutils==0.14 # via botocore
|
|||||||
dogpile.cache==0.6.5 # via openstacksdk
|
dogpile.cache==0.6.5 # via openstacksdk
|
||||||
entrypoints==0.2.3 # via keyring
|
entrypoints==0.2.3 # via keyring
|
||||||
enum34==1.1.6; python_version < '3' # via cryptography, knack, msrest, ovirt-engine-sdk-python
|
enum34==1.1.6; python_version < '3' # via cryptography, knack, msrest, ovirt-engine-sdk-python
|
||||||
|
futures==3.2.0 # via openstacksdk, s3transfer
|
||||||
|
google-auth==1.6.2
|
||||||
humanfriendly==4.8 # via azure-cli-core
|
humanfriendly==4.8 # via azure-cli-core
|
||||||
idna==2.6 # via cryptography, requests
|
idna==2.6 # via cryptography, requests
|
||||||
ipaddress==1.0.19 # via cryptography, openstacksdk
|
ipaddress==1.0.19 # via cryptography, openstacksdk
|
||||||
@@ -81,6 +84,7 @@ pbr==3.1.1 # via keystoneauth1, openstacksdk, os-service-types, s
|
|||||||
pexpect==4.6.0
|
pexpect==4.6.0
|
||||||
psutil==5.4.3
|
psutil==5.4.3
|
||||||
ptyprocess==0.5.2 # via pexpect
|
ptyprocess==0.5.2 # via pexpect
|
||||||
|
pyasn1-modules==0.2.3 # via google-auth
|
||||||
pyasn1==0.4.2 # via paramiko
|
pyasn1==0.4.2 # via paramiko
|
||||||
pycparser==2.18 # via cffi
|
pycparser==2.18 # via cffi
|
||||||
pycurl==7.43.0.1 # via ovirt-engine-sdk-python
|
pycurl==7.43.0.1 # via ovirt-engine-sdk-python
|
||||||
@@ -100,11 +104,12 @@ requests-ntlm==1.1.0 # via pywinrm
|
|||||||
requests-oauthlib==0.8.0 # via msrest
|
requests-oauthlib==0.8.0 # via msrest
|
||||||
requests==2.20.0
|
requests==2.20.0
|
||||||
requestsexceptions==1.4.0 # via openstacksdk, os-client-config
|
requestsexceptions==1.4.0 # via openstacksdk, os-client-config
|
||||||
|
rsa==4.0 # via google-auth
|
||||||
s3transfer==0.1.13 # via boto3
|
s3transfer==0.1.13 # via boto3
|
||||||
secretstorage==2.3.1 # via keyring
|
secretstorage==2.3.1 # via keyring
|
||||||
selectors2==2.0.1 # via ncclient
|
selectors2==2.0.1 # via ncclient
|
||||||
shade==1.27.0
|
shade==1.27.0
|
||||||
six==1.11.0 # via azure-cli-core, bcrypt, cryptography, isodate, keystoneauth1, knack, munch, ncclient, ntlm-auth, openstacksdk, ovirt-engine-sdk-python, packaging, pynacl, pyopenssl, python-dateutil, pyvmomi, pywinrm, stevedore
|
six==1.11.0 # via azure-cli-core, bcrypt, cryptography, google-auth, isodate, keystoneauth1, knack, munch, ncclient, ntlm-auth, openstacksdk, ovirt-engine-sdk-python, packaging, pynacl, pyopenssl, python-dateutil, pyvmomi, pywinrm, stevedore
|
||||||
stevedore==1.28.0 # via keystoneauth1
|
stevedore==1.28.0 # via keystoneauth1
|
||||||
tabulate==0.7.7 # via azure-cli-core, knack
|
tabulate==0.7.7 # via azure-cli-core, knack
|
||||||
urllib3==1.24 # via requests
|
urllib3==1.24 # via requests
|
||||||
|
|||||||
Reference in New Issue
Block a user