Merge branch 'devel' of https://github.com/ansible/ansible-tower into 11th-hour

This commit is contained in:
AlanCoding
2016-04-18 14:12:13 -04:00
29 changed files with 878 additions and 294 deletions

View File

@@ -1675,7 +1675,9 @@ class JobTemplateSerializer(UnifiedJobTemplateSerializer, JobOptionsSerializer):
class Meta:
model = JobTemplate
fields = ('*', 'host_config_key', 'ask_variables_on_launch', 'survey_enabled', 'become_enabled')
fields = ('*', 'host_config_key', 'ask_variables_on_launch', 'ask_limit_on_launch',
'ask_tags_on_launch', 'ask_job_type_on_launch', 'ask_inventory_on_launch',
'ask_credential_on_launch', 'survey_enabled', 'become_enabled')
def get_related(self, obj):
res = super(JobTemplateSerializer, self).get_related(obj)
@@ -1732,10 +1734,16 @@ class JobSerializer(UnifiedJobSerializer, JobOptionsSerializer):
passwords_needed_to_start = serializers.ReadOnlyField()
ask_variables_on_launch = serializers.ReadOnlyField()
ask_limit_on_launch = serializers.ReadOnlyField()
ask_tags_on_launch = serializers.ReadOnlyField()
ask_job_type_on_launch = serializers.ReadOnlyField()
ask_inventory_on_launch = serializers.ReadOnlyField()
class Meta:
model = Job
fields = ('*', 'job_template', 'passwords_needed_to_start', 'ask_variables_on_launch')
fields = ('*', 'job_template', 'passwords_needed_to_start', 'ask_variables_on_launch',
'ask_limit_on_launch', 'ask_tags_on_launch', 'ask_job_type_on_launch',
'ask_inventory_on_launch')
def get_related(self, obj):
res = super(JobSerializer, self).get_related(obj)
@@ -2098,24 +2106,36 @@ class JobLaunchSerializer(BaseSerializer):
can_start_without_user_input = serializers.BooleanField(read_only=True)
variables_needed_to_start = serializers.ReadOnlyField()
credential_needed_to_start = serializers.SerializerMethodField()
inventory_needed_to_start = serializers.SerializerMethodField()
survey_enabled = serializers.SerializerMethodField()
extra_vars = VerbatimField(required=False, write_only=True)
class Meta:
model = JobTemplate
fields = ('can_start_without_user_input', 'passwords_needed_to_start', 'extra_vars',
'ask_variables_on_launch', 'survey_enabled', 'variables_needed_to_start',
'credential', 'credential_needed_to_start',)
read_only_fields = ('ask_variables_on_launch',)
fields = ('can_start_without_user_input', 'passwords_needed_to_start',
'extra_vars', 'limit', 'job_tags', 'skip_tags', 'job_type', 'inventory',
'credential', 'ask_variables_on_launch', 'ask_tags_on_launch',
'ask_job_type_on_launch', 'ask_inventory_on_launch', 'ask_limit_on_launch',
'survey_enabled', 'variables_needed_to_start',
'credential_needed_to_start', 'inventory_needed_to_start',)
read_only_fields = ('ask_variables_on_launch', 'ask_limit_on_launch',
'ask_tags_on_launch', 'ask_job_type_on_launch',
'ask_inventory_on_launch', 'ask_credential_on_launch')
extra_kwargs = {
'credential': {
'write_only': True,
},
'credential': {'write_only': True,},
'limit': {'write_only': True,},
'job_tags': {'write_only': True,},
'skip_tags': {'write_only': True,},
'job_type': {'write_only': True,},
'inventory': {'write_only': True,}
}
def get_credential_needed_to_start(self, obj):
return not (obj and obj.credential)
def get_inventory_needed_to_start(self, obj):
return not (obj and obj.inventory)
def get_survey_enabled(self, obj):
if obj:
return obj.survey_enabled and 'spec' in obj.survey_spec
@@ -2126,7 +2146,10 @@ class JobLaunchSerializer(BaseSerializer):
obj = self.context.get('obj')
data = self.context.get('data')
credential = attrs.get('credential', obj and obj.credential or None)
if (not obj.ask_credential_on_launch) or (not attrs.get('credential', None)):
credential = obj.credential
else:
credential = attrs.get('credential', None)
if not credential:
errors['credential'] = 'Credential not provided'
@@ -2160,13 +2183,27 @@ class JobLaunchSerializer(BaseSerializer):
if obj.job_type != PERM_INVENTORY_SCAN and (obj.project is None):
errors['project'] = 'Job Template Project is missing or undefined'
if obj.inventory is None:
if (obj.inventory is None) and not attrs.get('inventory', None):
errors['inventory'] = 'Job Template Inventory is missing or undefined'
if errors:
raise serializers.ValidationError(errors)
JT_extra_vars = obj.extra_vars
JT_limit = obj.limit
JT_job_type = obj.job_type
JT_job_tags = obj.job_tags
JT_skip_tags = obj.skip_tags
JT_inventory = obj.inventory
JT_credential = obj.credential
attrs = super(JobLaunchSerializer, self).validate(attrs)
obj.extra_vars = JT_extra_vars
obj.limit = JT_limit
obj.job_type = JT_job_type
obj.skip_tags = JT_skip_tags
obj.job_tags = JT_job_tags
obj.inventory = JT_inventory
obj.credential = JT_credential
return attrs
class NotifierSerializer(BaseSerializer):

View File

@@ -6,24 +6,40 @@ The response will include the following fields:
* `ask_variables_on_launch`: Flag indicating whether the job_template is
configured to prompt for variables upon launch (boolean, read-only)
* `ask_tags_on_launch`: Flag indicating whether the job_template is
configured to prompt for tags upon launch (boolean, read-only)
* `ask_job_type_on_launch`: Flag indicating whether the job_template is
configured to prompt for job_type upon launch (boolean, read-only)
* `ask_limit_on_launch`: Flag indicating whether the job_template is
configured to prompt for limit upon launch (boolean, read-only)
* `ask_inventory_on_launch`: Flag indicating whether the job_template is
configured to prompt for inventory upon launch (boolean, read-only)
* `ask_credential_on_launch`: Flag indicating whether the job_template is
configured to prompt for credential upon launch (boolean, read-only)
* `can_start_without_user_input`: Flag indicating if the job_template can be
launched without user-input (boolean, read-only)
* `passwords_needed_to_start`: Password names required to launch the
job_template (array, read-only)
* `variables_needed_to_start`: Required variable names required to launch the
job_template (array, read-only)
* `survey_enabled`: Flag indicating if whether the job_template has an enabled
* `survey_enabled`: Flag indicating whether the job_template has an enabled
survey (boolean, read-only)
* `credential_needed_to_start`: Flag indicating the presence of a credential
associated with the job template. If not then one should be supplied when
launching the job (boolean, read-only)
* `inventory_needed_to_start`: Flag indicating the presence of an inventory
associated with the job template. If not then one should be supplied when
launching the job (boolean, read-only)
Make a POST request to this resource to launch the job_template. If any
passwords or extra variables (extra_vars) are required, they must be passed
via POST data, with extra_vars given as a YAML or JSON string and escaped
parentheses. If `credential_needed_to_start` is `True` then the `credential`
field is required as well.
passwords, inventory, or extra variables (extra_vars) are required, they must
be passed via POST data, with extra_vars given as a YAML or JSON string and
escaped parentheses. If `credential_needed_to_start` is `True` then the
`credential` field is required and if the `inventory_needed_to_start` is
`True` then the `inventory` is required as well.
If successful, the response status code will be 202. If any required passwords
If successful, the response status code will be 201. If any required passwords
are not provided, a 400 status code will be returned. If the job cannot be
launched, a 405 status code will be returned.
launched, a 405 status code will be returned. If the provided credential or
inventory are not allowed to be used by the user, then a 403 status code will
be returned.

View File

@@ -2082,17 +2082,23 @@ class JobTemplateLaunch(RetrieveAPIView, GenericAPIView):
def update_raw_data(self, data):
obj = self.get_object()
extra_vars = data.get('extra_vars') or {}
extra_vars = data.pop('extra_vars', None) or {}
if obj:
for p in obj.passwords_needed_to_start:
data[p] = u''
if obj.credential:
data.pop('credential', None)
else:
data['credential'] = None
for v in obj.variables_needed_to_start:
extra_vars.setdefault(v, u'')
data['extra_vars'] = extra_vars
if extra_vars:
data['extra_vars'] = extra_vars
ask_for_vars_dict = obj._ask_for_vars_dict()
ask_for_vars_dict.pop('extra_vars')
for field in ask_for_vars_dict:
if not ask_for_vars_dict[field]:
data.pop(field, None)
elif field == 'inventory' or field == 'credential':
data[field] = getattrd(obj, "%s.%s" % (field, 'id'), None)
else:
data[field] = getattr(obj, field)
return data
def post(self, request, *args, **kwargs):
@@ -2102,21 +2108,27 @@ class JobTemplateLaunch(RetrieveAPIView, GenericAPIView):
if 'credential' not in request.data and 'credential_id' in request.data:
request.data['credential'] = request.data['credential_id']
if 'inventory' not in request.data and 'inventory_id' in request.data:
request.data['inventory'] = request.data['inventory_id']
passwords = {}
serializer = self.serializer_class(instance=obj, data=request.data, context={'obj': obj, 'data': request.data, 'passwords': passwords})
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# At this point, a credential is gauranteed to exist at serializer.instance.credential
if not request.user.can_access(Credential, 'read', serializer.instance.credential):
raise PermissionDenied()
prompted_fields, ignored_fields = obj._accept_or_ignore_job_kwargs(**request.data)
kv = {
'credential': serializer.instance.credential.pk,
}
if 'extra_vars' in request.data:
kv['extra_vars'] = request.data['extra_vars']
if 'credential' in prompted_fields and prompted_fields['credential'] != getattrd(obj, 'credential.pk', None):
new_credential = Credential.objects.get(pk=prompted_fields['credential'])
if not request.user.can_access(Credential, 'use', new_credential):
raise PermissionDenied()
if 'inventory' in prompted_fields and prompted_fields['inventory'] != getattrd(obj, 'inventory.pk', None):
new_inventory = Inventory.objects.get(pk=prompted_fields['inventory'])
if not request.user.can_access(Inventory, 'use', new_inventory):
raise PermissionDenied()
kv = prompted_fields
kv.update(passwords)
new_job = obj.create_unified_job(**kv)
@@ -2126,8 +2138,11 @@ class JobTemplateLaunch(RetrieveAPIView, GenericAPIView):
new_job.delete()
return Response(data, status=status.HTTP_400_BAD_REQUEST)
else:
data = dict(job=new_job.id)
return Response(data, status=status.HTTP_202_ACCEPTED)
data = OrderedDict()
data['ignored_fields'] = ignored_fields
data.update(JobSerializer(new_job).to_representation(new_job))
data['job'] = new_job.id
return Response(data, status=status.HTTP_201_CREATED)
class JobTemplateSchedulesList(SubListCreateAttachDetachAPIView):
@@ -2411,7 +2426,7 @@ class JobTemplateCallback(GenericAPIView):
# Return the location of the new job.
headers = {'Location': job.get_absolute_url()}
return Response(status=status.HTTP_202_ACCEPTED, headers=headers)
return Response(status=status.HTTP_201_CREATED, headers=headers)
class JobTemplateJobsList(SubListCreateAPIView):
@@ -2459,7 +2474,7 @@ class SystemJobTemplateLaunch(GenericAPIView):
new_job = obj.create_unified_job(**request.data)
new_job.signal_start(**request.data)
data = dict(system_job=new_job.id)
return Response(data, status=status.HTTP_202_ACCEPTED)
return Response(data, status=status.HTTP_201_CREATED)
class SystemJobTemplateSchedulesList(SubListCreateAttachDetachAPIView):