mirror of
https://github.com/ZwareBear/awx.git
synced 2026-05-17 07:48:39 -05:00
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:
+77
-36
@@ -52,7 +52,7 @@ from awx.main.access import access_registry
|
||||
from awx.main.models import (
|
||||
Schedule, TowerScheduleState, Instance, InstanceGroup,
|
||||
UnifiedJob, Notification,
|
||||
Inventory, SmartInventoryMembership,
|
||||
Inventory, InventorySource, SmartInventoryMembership,
|
||||
Job, AdHocCommand, ProjectUpdate, InventoryUpdate, SystemJob,
|
||||
JobEvent, ProjectUpdateEvent, InventoryUpdateEvent, AdHocCommandEvent, SystemJobEvent,
|
||||
build_safe_env
|
||||
@@ -67,6 +67,7 @@ from awx.main.utils import (get_ssh_version, update_scm_url,
|
||||
get_licenser,
|
||||
ignore_inventory_computed_fields,
|
||||
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.reload import stop_local_services
|
||||
from awx.main.utils.pglock import advisory_lock
|
||||
@@ -713,12 +714,25 @@ class BaseTask(object):
|
||||
logger.error('Failed to update %s after %d retries.',
|
||||
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):
|
||||
'''
|
||||
Return absolute path relative to this file.
|
||||
'''
|
||||
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):
|
||||
'''
|
||||
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):
|
||||
"""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
|
||||
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,
|
||||
private_data_dir,
|
||||
@@ -2145,6 +2163,7 @@ class RunInventoryUpdate(BaseTask):
|
||||
if private_data_files is None:
|
||||
private_data_files = {}
|
||||
self.add_awx_venv(env)
|
||||
self.add_ansible_venv(inventory_update.ansible_virtualenv_path, env)
|
||||
# Pass inventory source ID to inventory script.
|
||||
env['INVENTORY_SOURCE_ID'] = str(inventory_update.inventory_source_id)
|
||||
env['INVENTORY_UPDATE_ID'] = str(inventory_update.pk)
|
||||
@@ -2176,25 +2195,19 @@ class RunInventoryUpdate(BaseTask):
|
||||
inventory_update.get_cloud_credential(), ''
|
||||
)
|
||||
|
||||
if inventory_update.source == 'gce':
|
||||
env['GCE_ZONE'] = inventory_update.source_regions if inventory_update.source_regions != 'all' else '' # noqa
|
||||
if inventory_update.source in InventorySource.injectors:
|
||||
# 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
|
||||
# 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
|
||||
elif inventory_update.source in ['scm', 'custom']:
|
||||
if inventory_update.source == 'tower':
|
||||
env['TOWER_INVENTORY'] = inventory_update.instance_filters
|
||||
env['TOWER_LICENSE_TYPE'] = get_licenser().validate()['license_type']
|
||||
|
||||
if inventory_update.source in ['scm', 'custom']:
|
||||
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:
|
||||
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':
|
||||
raise NotImplementedError('Cannot update file sources through the task system.')
|
||||
return env
|
||||
@@ -2259,33 +2272,61 @@ class RunInventoryUpdate(BaseTask):
|
||||
getattr(settings, '%s_INSTANCE_ID_VAR' % src.upper()),])
|
||||
# Add arguments for the source inventory script
|
||||
args.append('--source')
|
||||
if src in CLOUD_PROVIDERS:
|
||||
# Get the path to the inventory plugin, and append it to our
|
||||
# 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(self.build_inventory(inventory_update, private_data_dir))
|
||||
if src == 'custom':
|
||||
args.append("--custom")
|
||||
args.append('-v%d' % inventory_update.verbosity)
|
||||
if settings.DEBUG:
|
||||
args.append('--traceback')
|
||||
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):
|
||||
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 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):
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user