mirror of
https://github.com/ansible/awx.git
synced 2026-05-13 20:37:39 -02:30
new credential passwords system on relaunch
This commit is contained in:
@@ -3230,17 +3230,20 @@ class JobRelaunchSerializer(BaseSerializer):
|
|||||||
],
|
],
|
||||||
write_only=True
|
write_only=True
|
||||||
)
|
)
|
||||||
|
credential_passwords = VerbatimField(required=True, write_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Job
|
model = Job
|
||||||
fields = ('passwords_needed_to_start', 'retry_counts', 'hosts',)
|
fields = ('passwords_needed_to_start', 'retry_counts', 'hosts', 'credential_passwords',)
|
||||||
|
|
||||||
def to_internal_value(self, data):
|
def validate_credential_passwords(self, value):
|
||||||
obj = self.context.get('obj')
|
pnts = self.instance.passwords_needed_to_start
|
||||||
all_data = self.to_representation(obj)
|
missing = set(pnts) - set(key for key in value if value[key])
|
||||||
all_data.update(data)
|
if missing:
|
||||||
ret = super(JobRelaunchSerializer, self).to_internal_value(all_data)
|
raise serializers.ValidationError(_(
|
||||||
return ret
|
'Missing passwords needed to start: {}'.format(', '.join(missing))
|
||||||
|
))
|
||||||
|
return value
|
||||||
|
|
||||||
def to_representation(self, obj):
|
def to_representation(self, obj):
|
||||||
res = super(JobRelaunchSerializer, self).to_representation(obj)
|
res = super(JobRelaunchSerializer, self).to_representation(obj)
|
||||||
@@ -3263,24 +3266,17 @@ class JobRelaunchSerializer(BaseSerializer):
|
|||||||
data[status] = obj.retry_qs(status).count()
|
data[status] = obj.retry_qs(status).count()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def validate_passwords_needed_to_start(self, value):
|
def get_validation_exclusions(self, *args, **kwargs):
|
||||||
obj = self.context.get('obj')
|
r = super(JobRelaunchSerializer, self).get_validation_exclusions(*args, **kwargs)
|
||||||
data = self.context.get('data')
|
r.append('credential_passwords')
|
||||||
|
return r
|
||||||
# Check for passwords needed
|
|
||||||
needed = self.get_passwords_needed_to_start(obj)
|
|
||||||
provided = dict([(field, data.get(field, '')) for field in needed])
|
|
||||||
if not all(provided.values()):
|
|
||||||
raise serializers.ValidationError(needed)
|
|
||||||
return value
|
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
obj = self.context.get('obj')
|
obj = self.instance
|
||||||
if obj.project is None:
|
if obj.project is None:
|
||||||
raise serializers.ValidationError(dict(errors=[_("Job Template Project is missing or undefined.")]))
|
raise serializers.ValidationError(dict(errors=[_("Job Template Project is missing or undefined.")]))
|
||||||
if obj.inventory is None or obj.inventory.pending_deletion:
|
if obj.inventory is None or obj.inventory.pending_deletion:
|
||||||
raise serializers.ValidationError(dict(errors=[_("Job Template Inventory is missing or undefined.")]))
|
raise serializers.ValidationError(dict(errors=[_("Job Template Inventory is missing or undefined.")]))
|
||||||
attrs.pop('hosts', None)
|
|
||||||
attrs = super(JobRelaunchSerializer, self).validate(attrs)
|
attrs = super(JobRelaunchSerializer, self).validate(attrs)
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|||||||
@@ -3442,6 +3442,7 @@ class JobTemplateCallback(GenericAPIView):
|
|||||||
result = job.signal_start(inventory_sources_already_updated=inventory_sources_already_updated)
|
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!'))
|
||||||
|
job.delete()
|
||||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
# Return the location of the new job.
|
# Return the location of the new job.
|
||||||
@@ -4108,6 +4109,22 @@ class JobRelaunch(RetrieveAPIView):
|
|||||||
obj_permission_type = 'start'
|
obj_permission_type = 'start'
|
||||||
serializer_class = JobRelaunchSerializer
|
serializer_class = JobRelaunchSerializer
|
||||||
|
|
||||||
|
def update_raw_data(self, data):
|
||||||
|
data = super(JobRelaunch, self).update_raw_data(data)
|
||||||
|
try:
|
||||||
|
obj = self.get_object()
|
||||||
|
except PermissionDenied:
|
||||||
|
return data
|
||||||
|
if obj:
|
||||||
|
needed_passwords = obj.passwords_needed_to_start
|
||||||
|
if needed_passwords:
|
||||||
|
data['credential_passwords'] = {}
|
||||||
|
for p in needed_passwords:
|
||||||
|
data['credential_passwords'][p] = u''
|
||||||
|
else:
|
||||||
|
data.pop('credential_passwords')
|
||||||
|
return data
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@transaction.non_atomic_requests
|
@transaction.non_atomic_requests
|
||||||
def dispatch(self, *args, **kwargs):
|
def dispatch(self, *args, **kwargs):
|
||||||
@@ -4122,15 +4139,22 @@ class JobRelaunch(RetrieveAPIView):
|
|||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
|
context = self.get_serializer_context()
|
||||||
|
|
||||||
|
modified_data = request.data.copy()
|
||||||
|
modified_data.setdefault('credential_passwords', {})
|
||||||
|
for password in obj.passwords_needed_to_start:
|
||||||
|
if password in modified_data:
|
||||||
|
modified_data['credential_passwords'][password] = modified_data[password]
|
||||||
|
|
||||||
# Note: is_valid() may modify request.data
|
# Note: is_valid() may modify request.data
|
||||||
# It will remove any key/value pair who's key is not in the 'passwords_needed_to_start' list
|
# It will remove any key/value pair who's key is not in the 'passwords_needed_to_start' list
|
||||||
serializer = self.serializer_class(data=request.data, context={'obj': obj, 'data': request.data})
|
serializer = self.serializer_class(data=modified_data, context=context, instance=obj)
|
||||||
if not serializer.is_valid():
|
if not serializer.is_valid():
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
copy_kwargs = {}
|
copy_kwargs = {}
|
||||||
retry_hosts = request.data.get('hosts', None)
|
retry_hosts = serializer.validated_data.get('hosts', None)
|
||||||
if retry_hosts and retry_hosts != 'all':
|
if retry_hosts and retry_hosts != 'all':
|
||||||
if obj.status in ACTIVE_STATES:
|
if obj.status in ACTIVE_STATES:
|
||||||
return Response({'hosts': _(
|
return Response({'hosts': _(
|
||||||
@@ -4149,12 +4173,13 @@ class JobRelaunch(RetrieveAPIView):
|
|||||||
copy_kwargs['limit'] = ','.join(retry_host_list)
|
copy_kwargs['limit'] = ','.join(retry_host_list)
|
||||||
|
|
||||||
new_job = obj.copy_unified_job(**copy_kwargs)
|
new_job = obj.copy_unified_job(**copy_kwargs)
|
||||||
result = new_job.signal_start(**request.data)
|
result = new_job.signal_start(**serializer.validated_data['credential_passwords'])
|
||||||
if not result:
|
if not result:
|
||||||
data = dict(passwords_needed_to_start=new_job.passwords_needed_to_start)
|
data = dict(msg=_('Error starting job!'))
|
||||||
|
new_job.delete()
|
||||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||||
else:
|
else:
|
||||||
data = JobSerializer(new_job, context=self.get_serializer_context()).data
|
data = JobSerializer(new_job, context=context).data
|
||||||
# Add job key to match what old relaunch returned.
|
# Add job key to match what old relaunch returned.
|
||||||
data['job'] = new_job.id
|
data['job'] = new_job.id
|
||||||
headers = {'Location': new_job.get_absolute_url(request=request)}
|
headers = {'Location': new_job.get_absolute_url(request=request)}
|
||||||
@@ -4387,6 +4412,7 @@ class AdHocCommandList(ListCreateAPIView):
|
|||||||
result = ad_hoc_command.signal_start(**request.data)
|
result = ad_hoc_command.signal_start(**request.data)
|
||||||
if not result:
|
if not result:
|
||||||
data = dict(passwords_needed_to_start=ad_hoc_command.passwords_needed_to_start)
|
data = dict(passwords_needed_to_start=ad_hoc_command.passwords_needed_to_start)
|
||||||
|
ad_hoc_command.delete()
|
||||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
@@ -4479,6 +4505,7 @@ class AdHocCommandRelaunch(GenericAPIView):
|
|||||||
result = new_ad_hoc_command.signal_start(**request.data)
|
result = new_ad_hoc_command.signal_start(**request.data)
|
||||||
if not result:
|
if not result:
|
||||||
data = dict(passwords_needed_to_start=new_ad_hoc_command.passwords_needed_to_start)
|
data = dict(passwords_needed_to_start=new_ad_hoc_command.passwords_needed_to_start)
|
||||||
|
new_ad_hoc_command.delete()
|
||||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||||
else:
|
else:
|
||||||
data = AdHocCommandSerializer(new_ad_hoc_command, context=self.get_serializer_context()).data
|
data = AdHocCommandSerializer(new_ad_hoc_command, context=self.get_serializer_context()).data
|
||||||
|
|||||||
Reference in New Issue
Block a user