mirror of
https://github.com/ansible/awx.git
synced 2026-03-02 01:08:48 -03:30
add fact modified time
This commit is contained in:
@@ -228,6 +228,19 @@ register(
|
|||||||
category_slug='jobs',
|
category_slug='jobs',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
register(
|
||||||
|
'ANSIBLE_FACT_CACHE_TIMEOUT',
|
||||||
|
field_class=fields.IntegerField,
|
||||||
|
min_value=0,
|
||||||
|
default=0,
|
||||||
|
label=_('Per-Host Ansible Fact Cache Timeout'),
|
||||||
|
help_text=_('Maximum time, in seconds, that Tower stored Ansible facts are considered valid since '
|
||||||
|
'the last time they were modified. Only valid, non-stale, facts will be accessible by '
|
||||||
|
'a playbook. Note, this does not influence the deletion of ansible_facts from the database.'),
|
||||||
|
category=_('Jobs'),
|
||||||
|
category_slug='jobs',
|
||||||
|
)
|
||||||
|
|
||||||
register(
|
register(
|
||||||
'LOG_AGGREGATOR_HOST',
|
'LOG_AGGREGATOR_HOST',
|
||||||
field_class=fields.CharField,
|
field_class=fields.CharField,
|
||||||
|
|||||||
@@ -81,6 +81,11 @@ class Migration(migrations.Migration):
|
|||||||
name='ansible_facts',
|
name='ansible_facts',
|
||||||
field=awx.main.fields.JSONBField(default={}, help_text='Arbitrary JSON structure of most recent ansible_facts, per-host.', blank=True),
|
field=awx.main.fields.JSONBField(default={}, help_text='Arbitrary JSON structure of most recent ansible_facts, per-host.', blank=True),
|
||||||
),
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='host',
|
||||||
|
name='ansible_facts_modified',
|
||||||
|
field=models.DateTimeField(default=None, help_text='The date and time ansible_facts was last modified.', null=True, editable=False),
|
||||||
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='job',
|
model_name='job',
|
||||||
name='store_facts',
|
name='store_facts',
|
||||||
|
|||||||
@@ -448,6 +448,12 @@ class Host(CommonModelNameNotUnique):
|
|||||||
default={},
|
default={},
|
||||||
help_text=_('Arbitrary JSON structure of most recent ansible_facts, per-host.'),
|
help_text=_('Arbitrary JSON structure of most recent ansible_facts, per-host.'),
|
||||||
)
|
)
|
||||||
|
ansible_facts_modified = models.DateTimeField(
|
||||||
|
default=None,
|
||||||
|
editable=False,
|
||||||
|
null=True,
|
||||||
|
help_text=_('The date and time ansible_facts was last modified.'),
|
||||||
|
)
|
||||||
insights_system_id = models.TextField(
|
insights_system_id = models.TextField(
|
||||||
blank=True,
|
blank=True,
|
||||||
default=None,
|
default=None,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import hashlib
|
|||||||
import hmac
|
import hmac
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
import json
|
||||||
from urlparse import urljoin
|
from urlparse import urljoin
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
@@ -707,12 +708,6 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin):
|
|||||||
self.project_update.cancel(job_explanation=job_explanation)
|
self.project_update.cancel(job_explanation=job_explanation)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@property
|
|
||||||
def store_facts_enabled(self):
|
|
||||||
if not self.job_template or self.job_template is False:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def memcached_fact_key(self):
|
def memcached_fact_key(self):
|
||||||
return '{}'.format(self.inventory.id)
|
return '{}'.format(self.inventory.id)
|
||||||
@@ -742,7 +737,7 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin):
|
|||||||
modified_key = self.memcached_fact_modified_key(host.name)
|
modified_key = self.memcached_fact_modified_key(host.name)
|
||||||
# Only add host/facts if host doesn't already exist in the cache
|
# Only add host/facts if host doesn't already exist in the cache
|
||||||
if cache.get(modified_key) is None:
|
if cache.get(modified_key) is None:
|
||||||
cache.set(host_key, host.ansible_facts)
|
cache.set(host_key, json.dumps(host.ansible_facts))
|
||||||
cache.set(modified_key, False)
|
cache.set(modified_key, False)
|
||||||
|
|
||||||
host_names.append(host.name)
|
host_names.append(host.name)
|
||||||
|
|||||||
@@ -773,6 +773,7 @@ class BaseTask(Task):
|
|||||||
self.final_run_hook(instance, status, **kwargs)
|
self.final_run_hook(instance, status, **kwargs)
|
||||||
instance.websocket_emit_status(status)
|
instance.websocket_emit_status(status)
|
||||||
if status != 'successful' and not hasattr(settings, 'CELERY_UNIT_TEST'):
|
if status != 'successful' and not hasattr(settings, 'CELERY_UNIT_TEST'):
|
||||||
|
print("Status is not successful!")
|
||||||
# Raising an exception will mark the job as 'failed' in celery
|
# Raising an exception will mark the job as 'failed' in celery
|
||||||
# and will stop a task chain from continuing to execute
|
# and will stop a task chain from continuing to execute
|
||||||
if status == 'canceled':
|
if status == 'canceled':
|
||||||
@@ -877,9 +878,12 @@ class RunJob(BaseTask):
|
|||||||
# callbacks to work.
|
# callbacks to work.
|
||||||
env['JOB_ID'] = str(job.pk)
|
env['JOB_ID'] = str(job.pk)
|
||||||
env['INVENTORY_ID'] = str(job.inventory.pk)
|
env['INVENTORY_ID'] = str(job.inventory.pk)
|
||||||
if job.store_facts_enabled:
|
if job.store_facts:
|
||||||
env['MEMCACHED_PREPEND_KEY'] = job.memcached_fact_key
|
env['ANSIBLE_LIBRARY'] = self.get_path_to('..', 'plugins', 'library')
|
||||||
env['MEMCACHED_LOCATION'] = settings.CACHES['default']['LOCATION']
|
env['ANSIBLE_CACHE_PLUGINS'] = self.get_path_to('..', 'plugins', 'fact_caching')
|
||||||
|
env['ANSIBLE_CACHE_PLUGIN'] = "tower"
|
||||||
|
env['ANSIBLE_FACT_CACHE_TIMEOUT'] = str(settings.ANSIBLE_FACT_CACHE_TIMEOUT)
|
||||||
|
env['ANSIBLE_CACHE_PLUGIN_CONNECTION'] = settings.CACHES['default']['LOCATION'] if 'LOCATION' in settings.CACHES['default'] else ''
|
||||||
if job.project:
|
if job.project:
|
||||||
env['PROJECT_REVISION'] = job.project.scm_revision
|
env['PROJECT_REVISION'] = job.project.scm_revision
|
||||||
env['ANSIBLE_RETRY_FILES_ENABLED'] = "False"
|
env['ANSIBLE_RETRY_FILES_ENABLED'] = "False"
|
||||||
@@ -954,13 +958,6 @@ class RunJob(BaseTask):
|
|||||||
if authorize:
|
if authorize:
|
||||||
env['ANSIBLE_NET_AUTH_PASS'] = decrypt_field(network_cred, 'authorize_password')
|
env['ANSIBLE_NET_AUTH_PASS'] = decrypt_field(network_cred, 'authorize_password')
|
||||||
|
|
||||||
# Set environment variables related to gathering facts from the cache
|
|
||||||
if (job.job_type == PERM_INVENTORY_SCAN or job.store_facts is True) and not kwargs.get('isolated'):
|
|
||||||
env['FACT_QUEUE'] = settings.FACT_QUEUE
|
|
||||||
env['ANSIBLE_LIBRARY'] = self.get_path_to('..', 'plugins', 'library')
|
|
||||||
env['ANSIBLE_CACHE_PLUGINS'] = self.get_path_to('..', 'plugins', 'fact_caching')
|
|
||||||
env['ANSIBLE_CACHE_PLUGIN'] = "tower"
|
|
||||||
env['ANSIBLE_CACHE_PLUGIN_CONNECTION'] = "tcp://127.0.0.1:%s" % str(settings.FACT_CACHE_PORT)
|
|
||||||
return env
|
return env
|
||||||
|
|
||||||
def build_args(self, job, **kwargs):
|
def build_args(self, job, **kwargs):
|
||||||
@@ -1143,13 +1140,13 @@ class RunJob(BaseTask):
|
|||||||
('project_update', local_project_sync.name, local_project_sync.id)))
|
('project_update', local_project_sync.name, local_project_sync.id)))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if job.store_facts_enabled:
|
if job.store_facts:
|
||||||
job.start_job_fact_cache()
|
job.start_job_fact_cache()
|
||||||
|
|
||||||
|
|
||||||
def final_run_hook(self, job, status, **kwargs):
|
def final_run_hook(self, job, status, **kwargs):
|
||||||
super(RunJob, self).final_run_hook(job, status, **kwargs)
|
super(RunJob, self).final_run_hook(job, status, **kwargs)
|
||||||
if job.store_facts_enabled:
|
if job.store_facts:
|
||||||
job.finish_job_fact_cache()
|
job.finish_job_fact_cache()
|
||||||
try:
|
try:
|
||||||
inventory = job.inventory
|
inventory = job.inventory
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ import os
|
|||||||
import memcache
|
import memcache
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from ansible import constants as C
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from ansible.cache.base import BaseCacheModule
|
from ansible.cache.base import BaseCacheModule
|
||||||
except:
|
except:
|
||||||
@@ -42,8 +44,8 @@ except:
|
|||||||
class CacheModule(BaseCacheModule):
|
class CacheModule(BaseCacheModule):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
# Basic in-memory caching for typical runs
|
self.mc = memcache.Client([C.CACHE_PLUGIN_CONNECTION], debug=0)
|
||||||
self.mc = memcache.Client([os.environ['MEMCACHED_LOCATION']], debug=0)
|
self.timeout = int(C.CACHE_PLUGIN_TIMEOUT)
|
||||||
self.inventory_id = os.environ['INVENTORY_ID']
|
self.inventory_id = os.environ['INVENTORY_ID']
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -59,9 +61,14 @@ class CacheModule(BaseCacheModule):
|
|||||||
def get(self, key):
|
def get(self, key):
|
||||||
host_key = self.translate_host_key(key)
|
host_key = self.translate_host_key(key)
|
||||||
value_json = self.mc.get(host_key)
|
value_json = self.mc.get(host_key)
|
||||||
if not value_json:
|
if value_json is None:
|
||||||
|
raise KeyError
|
||||||
|
try:
|
||||||
|
return json.loads(value_json)
|
||||||
|
# If cache entry is corrupt or bad, fail gracefully.
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
self.delete(key)
|
||||||
raise KeyError
|
raise KeyError
|
||||||
return json.loads(value_json)
|
|
||||||
|
|
||||||
def set(self, key, value):
|
def set(self, key, value):
|
||||||
host_key = self.translate_host_key(key)
|
host_key = self.translate_host_key(key)
|
||||||
@@ -74,7 +81,8 @@ class CacheModule(BaseCacheModule):
|
|||||||
return self.mc.get(self.host_names_key)
|
return self.mc.get(self.host_names_key)
|
||||||
|
|
||||||
def contains(self, key):
|
def contains(self, key):
|
||||||
val = self.mc.get(key)
|
host_key = self.translate_host_key(key)
|
||||||
|
val = self.mc.get(host_key)
|
||||||
if val is None:
|
if val is None:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@@ -84,13 +92,19 @@ class CacheModule(BaseCacheModule):
|
|||||||
self.mc.delete(self.translate_modified_key(key))
|
self.mc.delete(self.translate_modified_key(key))
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
for k in self.mc.get(self.host_names_key):
|
host_names = self.mc.get(self.host_names_key)
|
||||||
|
if not host_names:
|
||||||
|
return
|
||||||
|
|
||||||
|
for k in host_names:
|
||||||
self.mc.delete(self.translate_host_key(k))
|
self.mc.delete(self.translate_host_key(k))
|
||||||
self.mc.delete(self.translate_modified_key(k))
|
self.mc.delete(self.translate_modified_key(k))
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
ret = dict()
|
ret = dict()
|
||||||
for k in self.mc.get(self.host_names_key):
|
host_names = self.mc.get(self.host_names_key)
|
||||||
ret[k] = self.mc.get(self.translate_host_key(k))
|
if not host_names:
|
||||||
return ret
|
return
|
||||||
|
|
||||||
|
return [self.mc.get(self.translate_host_key(k)) for k in host_names]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user