Inventory plugins transition dev finishing work

Bump keystone auth to resolve problem with openstack script

Clarify code path, routing to template vs. managed injector
  behavior is also now reflected in test data files

Refactor test data layout for inventory injector logic

Add developer docs for inventory plugins transition

Memoize only get_ansible_version with no parameters

Make inventory plugin injector enablement a separate
  concept from the initial_version
  switch tests to look for plugin_name as well

Add plugin injectors for tower and foreman.

Add jinja2 native types compat feature

move tower source license compare logic to management command

introduce inventory source compat mode

pin jinja2 for native Ansible types

Add parent group keys, and additional translations

manual dash sanitization for un-region-like ec2 groups

nest zones under regions using Ansible core feature just merged
  implement conditionally only with BOTH group_by options

Make compat mode default be true
  in API models, UI add and edit controllers

Add several additional hostvars to translation
Add Azure tags null case translation

Make Azure group_by key off source_vars
  to be consistent with the script

support top-level ec2 boto_profile setting
This commit is contained in:
AlanCoding
2019-02-08 10:27:26 -05:00
parent bc5881ad21
commit cd7e358b73
64 changed files with 1248 additions and 476 deletions

View File

@@ -27,7 +27,6 @@ from awx.main.models.inventory import (
Host
)
from awx.main.utils.mem_inventory import MemInventory, dict_to_mem_data
from awx.main.utils.ansible import filter_non_json_lines
# other AWX imports
from awx.main.models.rbac import batch_role_ancestor_rebuilding
@@ -126,12 +125,12 @@ class AnsibleInventoryLoader(object):
def get_base_args(self):
# get ansible-inventory absolute path for running in bubblewrap/proot, in Popen
# NOTE: why do we add "python" to the start of these args?
# 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]
bargs = ['python', self.get_path_to_ansible_inventory(), '-i', self.source]
logger.debug('Using base command: {}'.format(' '.join(bargs)))
return bargs
@@ -174,21 +173,15 @@ class AnsibleInventoryLoader(object):
cmd = self.get_proot_args(cmd, env)
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
raw_stdout, stderr = proc.communicate()
raw_stdout = smart_text(raw_stdout)
stdout, stderr = proc.communicate()
stdout = smart_text(stdout)
stderr = smart_text(stderr)
if self.tmp_private_dir:
shutil.rmtree(self.tmp_private_dir, True)
if proc.returncode != 0:
raise RuntimeError('%s failed (rc=%d) with stdout:\n%s\nstderr:\n%s' % (
self.method, proc.returncode, raw_stdout, stderr))
# Openstack inventory plugin gives non-JSON lines
# Also, running with higher verbosity gives non-JSON lines
stdout = filter_non_json_lines(raw_stdout)
if stdout is not raw_stdout:
logger.warning('Output had lines stripped to obtain JSON format.')
self.method, proc.returncode, stdout, stderr))
for line in stderr.splitlines():
logger.error(line)
@@ -315,12 +308,6 @@ class Command(BaseCommand):
raise NotImplementedError('Value of enabled {} not understood.'.format(enabled))
def get_source_absolute_path(self, source):
# Sanity check: We sanitize these module names for our API but Ansible proper doesn't follow
# good naming conventions
source = source.replace('rhv.py', 'ovirt4.py')
source = source.replace('satellite6.py', 'foreman.py')
source = source.replace('vmware.py', 'vmware_inventory.py')
source = source.replace('openstack.py', 'openstack_inventory.py')
if not os.path.exists(source):
raise IOError('Source does not exist: %s' % source)
source = os.path.join(os.getcwd(), os.path.dirname(source),
@@ -893,12 +880,24 @@ class Command(BaseCommand):
self._create_update_group_children()
self._create_update_group_hosts()
def remote_tower_license_compare(self, local_license_type):
# this requires https://github.com/ansible/ansible/pull/52747
source_vars = self.all_group.variables
remote_license_type = source_vars.get('tower_metadata', {}).get('license_type', None)
if remote_license_type is None:
raise CommandError('Unexpected Error: Tower inventory plugin missing needed metadata!')
if local_license_type != remote_license_type:
raise CommandError('Tower server licenses must match: source: {} local: {}'.format(
remote_license_type, local_license_type
))
def check_license(self):
license_info = get_licenser().validate()
local_license_type = license_info.get('license_type', 'UNLICENSED')
if license_info.get('license_key', 'UNLICENSED') == 'UNLICENSED':
logger.error(LICENSE_NON_EXISTANT_MESSAGE)
raise CommandError('No license found!')
elif license_info.get('license_type', 'UNLICENSED') == 'open':
elif local_license_type == 'open':
return
available_instances = license_info.get('available_instances', 0)
free_instances = license_info.get('free_instances', 0)
@@ -907,6 +906,13 @@ class Command(BaseCommand):
if time_remaining <= 0 and not license_info.get('demo', False):
logger.error(LICENSE_EXPIRED_MESSAGE)
raise CommandError("License has expired!")
# special check for tower-type inventory sources
# but only if running the plugin
TOWER_SOURCE_FILES = ['tower.yml', 'tower.yaml']
if self.inventory_source.source == 'tower' and any(f in self.source for f in TOWER_SOURCE_FILES):
# only if this is the 2nd call to license check, we cannot compare before running plugin
if hasattr(self, 'all_group'):
self.remote_tower_license_compare(local_license_type)
if free_instances < 0:
d = {
'new_count': new_count,