mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 01:17:37 -02:30
Merge pull request #4564 from rooftopcellist/manifest_destiny
add collection version tracker & query info Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -1 +1 @@
|
|||||||
from .core import register, gather, ship # noqa
|
from .core import register, gather, ship, table_version # noqa
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ from awx.main.utils import (get_awx_version, get_ansible_version,
|
|||||||
get_custom_venv_choices, camelcase_to_underscore)
|
get_custom_venv_choices, camelcase_to_underscore)
|
||||||
from awx.main import models
|
from awx.main import models
|
||||||
from django.contrib.sessions.models import Session
|
from django.contrib.sessions.models import Session
|
||||||
from awx.main.analytics import register
|
from awx.main.analytics import register, table_version
|
||||||
|
|
||||||
'''
|
'''
|
||||||
This module is used to define metrics collected by awx.main.analytics.gather()
|
This module is used to define metrics collected by awx.main.analytics.gather()
|
||||||
Each function is decorated with a key name, and should return a data
|
Each function is decorated with a key name, and should return a data
|
||||||
structure that can be serialized to JSON
|
structure that can be serialized to JSON
|
||||||
|
|
||||||
@register('something')
|
@register('something', '1.0')
|
||||||
def something(since):
|
def something(since):
|
||||||
# the generated archive will contain a `something.json` w/ this JSON
|
# the generated archive will contain a `something.json` w/ this JSON
|
||||||
return {'some': 'json'}
|
return {'some': 'json'}
|
||||||
@@ -31,7 +31,7 @@ data _since_ the last report date - i.e., new data in the last 24 hours)
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
@register('config')
|
@register('config', '1.0')
|
||||||
def config(since):
|
def config(since):
|
||||||
license_info = get_license(show_key=False)
|
license_info = get_license(show_key=False)
|
||||||
install_type = 'traditional'
|
install_type = 'traditional'
|
||||||
@@ -62,7 +62,7 @@ def config(since):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@register('counts')
|
@register('counts', '1.0')
|
||||||
def counts(since):
|
def counts(since):
|
||||||
counts = {}
|
counts = {}
|
||||||
for cls in (models.Organization, models.Team, models.User,
|
for cls in (models.Organization, models.Team, models.User,
|
||||||
@@ -97,7 +97,7 @@ def counts(since):
|
|||||||
return counts
|
return counts
|
||||||
|
|
||||||
|
|
||||||
@register('org_counts')
|
@register('org_counts', '1.0')
|
||||||
def org_counts(since):
|
def org_counts(since):
|
||||||
counts = {}
|
counts = {}
|
||||||
for org in models.Organization.objects.annotate(num_users=Count('member_role__members', distinct=True),
|
for org in models.Organization.objects.annotate(num_users=Count('member_role__members', distinct=True),
|
||||||
@@ -109,7 +109,7 @@ def org_counts(since):
|
|||||||
return counts
|
return counts
|
||||||
|
|
||||||
|
|
||||||
@register('cred_type_counts')
|
@register('cred_type_counts', '1.0')
|
||||||
def cred_type_counts(since):
|
def cred_type_counts(since):
|
||||||
counts = {}
|
counts = {}
|
||||||
for cred_type in models.CredentialType.objects.annotate(num_credentials=Count(
|
for cred_type in models.CredentialType.objects.annotate(num_credentials=Count(
|
||||||
@@ -121,7 +121,7 @@ def cred_type_counts(since):
|
|||||||
return counts
|
return counts
|
||||||
|
|
||||||
|
|
||||||
@register('inventory_counts')
|
@register('inventory_counts', '1.0')
|
||||||
def inventory_counts(since):
|
def inventory_counts(since):
|
||||||
counts = {}
|
counts = {}
|
||||||
for inv in models.Inventory.objects.filter(kind='').annotate(num_sources=Count('inventory_sources', distinct=True),
|
for inv in models.Inventory.objects.filter(kind='').annotate(num_sources=Count('inventory_sources', distinct=True),
|
||||||
@@ -141,7 +141,7 @@ def inventory_counts(since):
|
|||||||
return counts
|
return counts
|
||||||
|
|
||||||
|
|
||||||
@register('projects_by_scm_type')
|
@register('projects_by_scm_type', '1.0')
|
||||||
def projects_by_scm_type(since):
|
def projects_by_scm_type(since):
|
||||||
counts = dict(
|
counts = dict(
|
||||||
(t[0] or 'manual', 0)
|
(t[0] or 'manual', 0)
|
||||||
@@ -160,7 +160,7 @@ def _get_isolated_datetime(last_check):
|
|||||||
return last_check
|
return last_check
|
||||||
|
|
||||||
|
|
||||||
@register('instance_info')
|
@register('instance_info', '1.0')
|
||||||
def instance_info(since, include_hostnames=False):
|
def instance_info(since, include_hostnames=False):
|
||||||
info = {}
|
info = {}
|
||||||
instances = models.Instance.objects.values_list('hostname').values(
|
instances = models.Instance.objects.values_list('hostname').values(
|
||||||
@@ -182,7 +182,7 @@ def instance_info(since, include_hostnames=False):
|
|||||||
return info
|
return info
|
||||||
|
|
||||||
|
|
||||||
@register('job_counts')
|
@register('job_counts', '1.0')
|
||||||
def job_counts(since):
|
def job_counts(since):
|
||||||
counts = {}
|
counts = {}
|
||||||
counts['total_jobs'] = models.UnifiedJob.objects.exclude(launch_type='sync').count()
|
counts['total_jobs'] = models.UnifiedJob.objects.exclude(launch_type='sync').count()
|
||||||
@@ -192,7 +192,7 @@ def job_counts(since):
|
|||||||
return counts
|
return counts
|
||||||
|
|
||||||
|
|
||||||
@register('job_instance_counts')
|
@register('job_instance_counts', '1.0')
|
||||||
def job_instance_counts(since):
|
def job_instance_counts(since):
|
||||||
counts = {}
|
counts = {}
|
||||||
job_types = models.UnifiedJob.objects.exclude(launch_type='sync').values_list(
|
job_types = models.UnifiedJob.objects.exclude(launch_type='sync').values_list(
|
||||||
@@ -207,7 +207,19 @@ def job_instance_counts(since):
|
|||||||
return counts
|
return counts
|
||||||
|
|
||||||
|
|
||||||
|
@register('query_info', '1.0')
|
||||||
|
def query_info(since, collection_type):
|
||||||
|
query_info = {}
|
||||||
|
query_info['last_run'] = str(since)
|
||||||
|
query_info['current_time'] = str(now())
|
||||||
|
query_info['collection_type'] = collection_type
|
||||||
|
return query_info
|
||||||
|
|
||||||
|
|
||||||
# Copies Job Events from db to a .csv to be shipped
|
# Copies Job Events from db to a .csv to be shipped
|
||||||
|
@table_version('events_table.csv', '1.0')
|
||||||
|
@table_version('unified_jobs_table.csv', '1.0')
|
||||||
|
@table_version('unified_job_template_table.csv', '1.0')
|
||||||
def copy_tables(since, full_path):
|
def copy_tables(since, full_path):
|
||||||
def _copy_table(table, query, path):
|
def _copy_table(table, query, path):
|
||||||
file_path = os.path.join(path, table + '_table.csv')
|
file_path = os.path.join(path, table + '_table.csv')
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ from awx.main.access import access_registry
|
|||||||
from awx.main.models.ha import TowerAnalyticsState
|
from awx.main.models.ha import TowerAnalyticsState
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['register', 'gather', 'ship']
|
__all__ = ['register', 'gather', 'ship', 'table_version']
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger('awx.main.analytics')
|
logger = logging.getLogger('awx.main.analytics')
|
||||||
|
|
||||||
|
manifest = dict()
|
||||||
|
|
||||||
|
|
||||||
def _valid_license():
|
def _valid_license():
|
||||||
try:
|
try:
|
||||||
@@ -35,25 +37,37 @@ def _valid_license():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def register(key):
|
def register(key, version):
|
||||||
"""
|
"""
|
||||||
A decorator used to register a function as a metric collector.
|
A decorator used to register a function as a metric collector.
|
||||||
|
|
||||||
Decorated functions should return JSON-serializable objects.
|
Decorated functions should return JSON-serializable objects.
|
||||||
|
|
||||||
@register('projects_by_scm_type')
|
@register('projects_by_scm_type', 1)
|
||||||
def projects_by_scm_type():
|
def projects_by_scm_type():
|
||||||
return {'git': 5, 'svn': 1, 'hg': 0}
|
return {'git': 5, 'svn': 1, 'hg': 0}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorate(f):
|
def decorate(f):
|
||||||
f.__awx_analytics_key__ = key
|
f.__awx_analytics_key__ = key
|
||||||
|
f.__awx_analytics_version__ = version
|
||||||
return f
|
return f
|
||||||
|
|
||||||
return decorate
|
return decorate
|
||||||
|
|
||||||
|
|
||||||
def gather(dest=None, module=None):
|
def table_version(file_name, version):
|
||||||
|
|
||||||
|
global manifest
|
||||||
|
manifest[file_name] = version
|
||||||
|
|
||||||
|
def decorate(f):
|
||||||
|
return f
|
||||||
|
|
||||||
|
return decorate
|
||||||
|
|
||||||
|
|
||||||
|
def gather(dest=None, module=None, collection_type='scheduled'):
|
||||||
"""
|
"""
|
||||||
Gather all defined metrics and write them as JSON files in a .tgz
|
Gather all defined metrics and write them as JSON files in a .tgz
|
||||||
|
|
||||||
@@ -84,18 +98,33 @@ def gather(dest=None, module=None):
|
|||||||
from awx.main.analytics import collectors
|
from awx.main.analytics import collectors
|
||||||
module = collectors
|
module = collectors
|
||||||
|
|
||||||
|
|
||||||
dest = dest or tempfile.mkdtemp(prefix='awx_analytics')
|
dest = dest or tempfile.mkdtemp(prefix='awx_analytics')
|
||||||
for name, func in inspect.getmembers(module):
|
for name, func in inspect.getmembers(module):
|
||||||
if inspect.isfunction(func) and hasattr(func, '__awx_analytics_key__'):
|
if inspect.isfunction(func) and hasattr(func, '__awx_analytics_key__'):
|
||||||
key = func.__awx_analytics_key__
|
key = func.__awx_analytics_key__
|
||||||
|
manifest['{}.json'.format(key)] = func.__awx_analytics_version__
|
||||||
path = '{}.json'.format(os.path.join(dest, key))
|
path = '{}.json'.format(os.path.join(dest, key))
|
||||||
with open(path, 'w', encoding='utf-8') as f:
|
with open(path, 'w', encoding='utf-8') as f:
|
||||||
try:
|
try:
|
||||||
json.dump(func(last_run), f)
|
if func.__name__ == 'query_info':
|
||||||
|
json.dump(func(last_run, collection_type=collection_type), f)
|
||||||
|
else:
|
||||||
|
json.dump(func(last_run), f)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("Could not generate metric {}.json".format(key))
|
logger.exception("Could not generate metric {}.json".format(key))
|
||||||
f.close()
|
f.close()
|
||||||
os.remove(f.name)
|
os.remove(f.name)
|
||||||
|
|
||||||
|
path = os.path.join(dest, 'manifest.json')
|
||||||
|
with open(path, 'w', encoding='utf-8') as f:
|
||||||
|
try:
|
||||||
|
json.dump(manifest, f)
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Could not generate manifest.json")
|
||||||
|
f.close()
|
||||||
|
os.remove(f.name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
collectors.copy_tables(since=last_run, full_path=dest)
|
collectors.copy_tables(since=last_run, full_path=dest)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class Command(BaseCommand):
|
|||||||
self.logger.propagate = False
|
self.logger.propagate = False
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
tgz = gather()
|
tgz = gather(collection_type='manual')
|
||||||
self.init_logging()
|
self.init_logging()
|
||||||
if tgz:
|
if tgz:
|
||||||
self.logger.debug(tgz)
|
self.logger.debug(tgz)
|
||||||
|
|||||||
@@ -9,17 +9,17 @@ from django.conf import settings
|
|||||||
from awx.main.analytics import gather, register
|
from awx.main.analytics import gather, register
|
||||||
|
|
||||||
|
|
||||||
@register('example')
|
@register('example', '1.0')
|
||||||
def example(since):
|
def example(since):
|
||||||
return {'awx': 123}
|
return {'awx': 123}
|
||||||
|
|
||||||
|
|
||||||
@register('bad_json')
|
@register('bad_json', '1.0')
|
||||||
def bad_json(since):
|
def bad_json(since):
|
||||||
return set()
|
return set()
|
||||||
|
|
||||||
|
|
||||||
@register('throws_error')
|
@register('throws_error', '1.0')
|
||||||
def throws_error(since):
|
def throws_error(since):
|
||||||
raise ValueError()
|
raise ValueError()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user