mirror of
https://github.com/ansible/awx.git
synced 2026-03-04 02:01:01 -03:30
Add refresh_inventory flag for job_template callback to refresh inventory before trying to find a matching host.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
The job template callback allows for empheral hosts to launch a new job.
|
The job template callback allows for ephemeral hosts to launch a new job.
|
||||||
|
|
||||||
Configure a host to POST to this resource, passing the `host_config_key`
|
Configure a host to POST to this resource, passing the `host_config_key`
|
||||||
parameter, to start a new job limited to only the requesting host. In the
|
parameter, to start a new job limited to only the requesting host. In the
|
||||||
@@ -18,6 +18,17 @@ The response will return status 202 if the request is valid, 403 for an
|
|||||||
invalid host config key, or 400 if the host cannot be determined from the
|
invalid host config key, or 400 if the host cannot be determined from the
|
||||||
address making the request.
|
address making the request.
|
||||||
|
|
||||||
|
_(New in Ansible Tower 2.0.0)_ By default, the host must already be present in
|
||||||
|
inventory for the callback to succeed. The `refresh_inventory` parameter can
|
||||||
|
be passed to the callback to trigger an inventory sync prior to searching for
|
||||||
|
the host and running the job. The associated inventory must have the
|
||||||
|
`update_on_launch` flag set and will only refresh if the `update_cache_timeout`
|
||||||
|
has expired.
|
||||||
|
|
||||||
|
For example, using curl:
|
||||||
|
|
||||||
|
curl --data-urlencode "host_config_key=HOST_CONFIG_KEY&refresh_inventory=1" http://server/api/v1/job_templates/N/callback/
|
||||||
|
|
||||||
A GET request may be used to verify that the correct host will be selected.
|
A GET request may be used to verify that the correct host will be selected.
|
||||||
This request must authenticate as a valid user with permission to edit the
|
This request must authenticate as a valid user with permission to edit the
|
||||||
job template. For example:
|
job template. For example:
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ from django.conf import settings
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.db.models import Q, Count, Sum
|
from django.db.models import Q, Count, Sum
|
||||||
|
from django.db import IntegrityError, transaction
|
||||||
from django.db import IntegrityError
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
@@ -1268,15 +1267,18 @@ class JobTemplateCallback(GenericAPIView):
|
|||||||
# Find the list of remote host names/IPs to check.
|
# Find the list of remote host names/IPs to check.
|
||||||
remote_hosts = set()
|
remote_hosts = set()
|
||||||
for header in settings.REMOTE_HOST_HEADERS:
|
for header in settings.REMOTE_HOST_HEADERS:
|
||||||
value = self.request.META.get(header, '').strip()
|
for value in self.request.META.get(header, '').split(','):
|
||||||
if value:
|
value = value.strip()
|
||||||
remote_hosts.add(value)
|
if value:
|
||||||
|
remote_hosts.add(value)
|
||||||
# Add the reverse lookup of IP addresses.
|
# Add the reverse lookup of IP addresses.
|
||||||
for rh in list(remote_hosts):
|
for rh in list(remote_hosts):
|
||||||
try:
|
try:
|
||||||
result = socket.gethostbyaddr(rh)
|
result = socket.gethostbyaddr(rh)
|
||||||
except socket.herror:
|
except socket.herror:
|
||||||
continue
|
continue
|
||||||
|
except socket.gaierror:
|
||||||
|
continue
|
||||||
remote_hosts.add(result[0])
|
remote_hosts.add(result[0])
|
||||||
remote_hosts.update(result[1])
|
remote_hosts.update(result[1])
|
||||||
# Filter out any .arpa results.
|
# Filter out any .arpa results.
|
||||||
@@ -1336,9 +1338,35 @@ class JobTemplateCallback(GenericAPIView):
|
|||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
job_template = self.get_object()
|
|
||||||
# Permission class should have already validated host_config_key.
|
# Permission class should have already validated host_config_key.
|
||||||
|
job_template = self.get_object()
|
||||||
|
# Attempt to find matching hosts based on remote address.
|
||||||
matching_hosts = self.find_matching_hosts()
|
matching_hosts = self.find_matching_hosts()
|
||||||
|
# If refresh_inventory flag is provided and the host is not found,
|
||||||
|
# update the inventory before trying to match the host.
|
||||||
|
refresh_inventory = request.DATA.get('refresh_inventory', '')
|
||||||
|
refresh_inventory = bool(refresh_inventory and refresh_inventory[0].lower() in ('t', 'y', '1'))
|
||||||
|
inventory_sources_already_updated = []
|
||||||
|
if refresh_inventory and len(matching_hosts) != 1:
|
||||||
|
inventory_sources = job_template.inventory.inventory_sources.filter(active=True, update_on_launch=True)
|
||||||
|
inventory_update_pks = set()
|
||||||
|
for inventory_source in inventory_sources:
|
||||||
|
if inventory_source.needs_update_on_launch:
|
||||||
|
# FIXME: Doesn't check for any existing updates.
|
||||||
|
inventory_update = inventory_source.create_inventory_update(launch_type='callback')
|
||||||
|
inventory_update.signal_start()
|
||||||
|
inventory_update_pks.add(inventory_update.pk)
|
||||||
|
inventory_update_qs = InventoryUpdate.objects.filter(pk__in=inventory_update_pks, status__in=('pending', 'waiting', 'running'))
|
||||||
|
# Poll for the inventory updates we've started to complete.
|
||||||
|
while inventory_update_qs.count():
|
||||||
|
time.sleep(1.0)
|
||||||
|
transaction.commit()
|
||||||
|
# Ignore failed inventory updates here, only add successful ones
|
||||||
|
# to the list to be excluded when running the job.
|
||||||
|
for inventory_update in InventoryUpdate.objects.filter(pk__in=inventory_update_pks, status='successful'):
|
||||||
|
inventory_sources_already_updated.append(inventory_update.inventory_source_id)
|
||||||
|
matching_hosts = self.find_matching_hosts()
|
||||||
|
# Check matching hosts.
|
||||||
if not matching_hosts:
|
if not matching_hosts:
|
||||||
data = dict(msg='No matching host could be found!')
|
data = dict(msg='No matching host could be found!')
|
||||||
# FIXME: Log!
|
# FIXME: Log!
|
||||||
@@ -1355,7 +1383,7 @@ class JobTemplateCallback(GenericAPIView):
|
|||||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||||
limit = ':&'.join(filter(None, [job_template.limit, host.name]))
|
limit = ':&'.join(filter(None, [job_template.limit, host.name]))
|
||||||
job = job_template.create_job(limit=limit, launch_type='callback')
|
job = job_template.create_job(limit=limit, launch_type='callback')
|
||||||
result = job.signal_start()
|
result = job.signal_start(inventory_sources_already_updated=inventory_sources_already_updated)
|
||||||
if not result:
|
if not result:
|
||||||
data = dict(msg='Error starting job!')
|
data = dict(msg='Error starting job!')
|
||||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|||||||
@@ -323,6 +323,18 @@ class Job(UnifiedJob, JobOptions):
|
|||||||
if type(obj) == InventoryUpdate:
|
if type(obj) == InventoryUpdate:
|
||||||
if obj.inventory_source in inventory_sources:
|
if obj.inventory_source in inventory_sources:
|
||||||
inventory_sources_found.append(obj.inventory_source)
|
inventory_sources_found.append(obj.inventory_source)
|
||||||
|
# Skip updating any inventory sources that were already updated before
|
||||||
|
# running this job (via callback inventory refresh).
|
||||||
|
try:
|
||||||
|
start_args = json.loads(decrypt_field(self, 'start_args'))
|
||||||
|
except Exception, e:
|
||||||
|
start_args = None
|
||||||
|
start_args = start_args or {}
|
||||||
|
inventory_sources_already_updated = start_args.get('inventory_sources_already_updated', [])
|
||||||
|
if inventory_sources_already_updated:
|
||||||
|
for source in inventory_sources.filter(pk__in=inventory_sources_already_updated):
|
||||||
|
if source not in inventory_sources_found:
|
||||||
|
inventory_sources_found.append(source)
|
||||||
if not project_found and self.project.needs_update_on_launch:
|
if not project_found and self.project.needs_update_on_launch:
|
||||||
dependencies.append(self.project.create_project_update(launch_type='dependency'))
|
dependencies.append(self.project.create_project_update(launch_type='dependency'))
|
||||||
if inventory_sources.count(): # and not has_setup_failures? Probably handled as an error scenario in the task runner
|
if inventory_sources.count(): # and not has_setup_failures? Probably handled as an error scenario in the task runner
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ angular.module('JobTemplatesHelper', ['Utilities'])
|
|||||||
scope.setCallbackHelp = function() {
|
scope.setCallbackHelp = function() {
|
||||||
scope.callback_help = "<p>With a provisioning callback URL and a host config key a host can contact Tower and request a configuration update using this job " +
|
scope.callback_help = "<p>With a provisioning callback URL and a host config key a host can contact Tower and request a configuration update using this job " +
|
||||||
"template. The request from the host must be a POST. Here is an example using curl:</p>\n" +
|
"template. The request from the host must be a POST. Here is an example using curl:</p>\n" +
|
||||||
"<pre>curl --data \"host_config_key=\"" + scope.example_config_key + "\" " +
|
"<pre>curl --data \"host_config_key=" + scope.example_config_key + "\" " +
|
||||||
scope.callback_server_path + GetBasePath('job_templates') + scope.example_template_id + "/callback/</pre>\n" +
|
scope.callback_server_path + GetBasePath('job_templates') + scope.example_template_id + "/callback/</pre>\n" +
|
||||||
"<p>Note the requesting host must be defined in the inventory associated with the job template. If Tower fails to " +
|
"<p>Note the requesting host must be defined in the inventory associated with the job template. If Tower fails to " +
|
||||||
"locate the host, the request will be denied.</p>" +
|
"locate the host, the request will be denied.</p>" +
|
||||||
|
|||||||
Reference in New Issue
Block a user