mirror of
https://github.com/ansible/awx.git
synced 2026-05-06 17:07:36 -02:30
Merge pull request #6168 from ryanpetrello/multicredential_job
Replace Job/JT cloud/network credentials with a single M2M relation.
This commit is contained in:
@@ -87,8 +87,7 @@ SUMMARIZABLE_FK_FIELDS = {
|
||||
'source_project': DEFAULT_SUMMARY_FIELDS + ('status', 'scm_type'),
|
||||
'project_update': DEFAULT_SUMMARY_FIELDS + ('status', 'failed',),
|
||||
'credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud'),
|
||||
'cloud_credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud'),
|
||||
'network_credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'net'),
|
||||
'vault_credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud'),
|
||||
'job': DEFAULT_SUMMARY_FIELDS + ('status', 'failed', 'elapsed'),
|
||||
'job_template': DEFAULT_SUMMARY_FIELDS,
|
||||
'workflow_job_template': DEFAULT_SUMMARY_FIELDS,
|
||||
@@ -1843,6 +1842,7 @@ class ResourceAccessListElementSerializer(UserSerializer):
|
||||
|
||||
class CredentialTypeSerializer(BaseSerializer):
|
||||
show_capabilities = ['edit', 'delete']
|
||||
managed_by_tower = serializers.ReadOnlyField()
|
||||
|
||||
class Meta:
|
||||
model = CredentialType
|
||||
@@ -1850,6 +1850,9 @@ class CredentialTypeSerializer(BaseSerializer):
|
||||
'injectors')
|
||||
|
||||
def validate(self, attrs):
|
||||
if self.instance and self.instance.managed_by_tower:
|
||||
raise serializers.ValidationError(
|
||||
{"detail": _("Modifications not allowed for credential types managed by Tower")})
|
||||
fields = attrs.get('inputs', {}).get('fields', [])
|
||||
for field in fields:
|
||||
if field.get('ask_at_runtime', False):
|
||||
@@ -2105,14 +2108,42 @@ class LabelsListMixin(object):
|
||||
return res
|
||||
|
||||
|
||||
# TODO: remove when API v1 is removed
|
||||
@six.add_metaclass(BaseSerializerMetaclass)
|
||||
class V1JobOptionsSerializer(BaseSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Credential
|
||||
fields = ('*', 'cloud_credential', 'network_credential')
|
||||
|
||||
V1_FIELDS = {
|
||||
'cloud_credential': models.PositiveIntegerField(blank=True, null=True, default=None),
|
||||
'network_credential': models.PositiveIntegerField(blank=True, null=True, default=None)
|
||||
}
|
||||
|
||||
def build_field(self, field_name, info, model_class, nested_depth):
|
||||
if field_name in self.V1_FIELDS:
|
||||
return self.build_standard_field(field_name,
|
||||
self.V1_FIELDS[field_name])
|
||||
return super(V1JobOptionsSerializer, self).build_field(field_name, info, model_class, nested_depth)
|
||||
|
||||
|
||||
class JobOptionsSerializer(LabelsListMixin, BaseSerializer):
|
||||
|
||||
class Meta:
|
||||
fields = ('*', 'job_type', 'inventory', 'project', 'playbook',
|
||||
'credential', 'cloud_credential', 'network_credential', 'forks', 'limit',
|
||||
'credential', 'vault_credential', 'forks', 'limit',
|
||||
'verbosity', 'extra_vars', 'job_tags', 'force_handlers',
|
||||
'skip_tags', 'start_at_task', 'timeout', 'store_facts',)
|
||||
|
||||
def get_fields(self):
|
||||
fields = super(JobOptionsSerializer, self).get_fields()
|
||||
|
||||
# TODO: remove when API v1 is removed
|
||||
if self.version == 1:
|
||||
fields.update(V1JobOptionsSerializer().get_fields())
|
||||
return fields
|
||||
|
||||
def get_related(self, obj):
|
||||
res = super(JobOptionsSerializer, self).get_related(obj)
|
||||
res['labels'] = self.reverse('api:job_template_label_list', kwargs={'pk': obj.pk})
|
||||
@@ -2122,12 +2153,19 @@ class JobOptionsSerializer(LabelsListMixin, BaseSerializer):
|
||||
res['project'] = self.reverse('api:project_detail', kwargs={'pk': obj.project.pk})
|
||||
if obj.credential:
|
||||
res['credential'] = self.reverse('api:credential_detail', kwargs={'pk': obj.credential.pk})
|
||||
if obj.cloud_credential:
|
||||
res['cloud_credential'] = self.reverse('api:credential_detail',
|
||||
kwargs={'pk': obj.cloud_credential.pk})
|
||||
if obj.network_credential:
|
||||
res['network_credential'] = self.reverse('api:credential_detail',
|
||||
kwargs={'pk': obj.network_credential.pk})
|
||||
if obj.vault_credential:
|
||||
res['vault_credential'] = self.reverse('api:credential_detail', kwargs={'pk': obj.vault_credential.pk})
|
||||
if self.version > 1:
|
||||
view = 'api:%s_extra_credentials_list' % camelcase_to_underscore(obj.__class__.__name__)
|
||||
res['extra_credentials'] = self.reverse(view, kwargs={'pk': obj.pk})
|
||||
else:
|
||||
cloud_cred = obj.cloud_credential
|
||||
if cloud_cred:
|
||||
res['cloud_credential'] = self.reverse('api:credential_detail', kwargs={'pk': cloud_cred})
|
||||
net_cred = obj.network_credential
|
||||
if net_cred:
|
||||
res['cloud_credential'] = self.reverse('api:credential_detail', kwargs={'pk': net_cred})
|
||||
|
||||
return res
|
||||
|
||||
def to_representation(self, obj):
|
||||
@@ -2142,13 +2180,38 @@ class JobOptionsSerializer(LabelsListMixin, BaseSerializer):
|
||||
ret['playbook'] = ''
|
||||
if 'credential' in ret and not obj.credential:
|
||||
ret['credential'] = None
|
||||
if 'cloud_credential' in ret and not obj.cloud_credential:
|
||||
ret['cloud_credential'] = None
|
||||
if 'network_credential' in ret and not obj.network_credential:
|
||||
ret['network_credential'] = None
|
||||
if 'vault_credential' in ret and not obj.vault_credential:
|
||||
ret['vault_credential'] = None
|
||||
if self.version == 1:
|
||||
ret['cloud_credential'] = obj.cloud_credential
|
||||
ret['network_credential'] = obj.network_credential
|
||||
return ret
|
||||
|
||||
def validate(self, attrs):
|
||||
if self.version == 1: # TODO: remove in 3.3
|
||||
if 'cloud_credential' in attrs:
|
||||
pk = attrs.pop('cloud_credential')
|
||||
for cred in self.instance.cloud_credentials:
|
||||
self.instance.extra_credentials.remove(cred)
|
||||
if pk:
|
||||
cred = Credential.objects.get(pk=pk)
|
||||
if cred.credential_type.kind != 'cloud':
|
||||
raise serializers.ValidationError({
|
||||
'cloud_credential': _('You must provide a cloud credential.'),
|
||||
})
|
||||
self.instance.extra_credentials.add(cred)
|
||||
if 'network_credential' in attrs:
|
||||
pk = attrs.pop('network_credential')
|
||||
for cred in self.instance.network_credentials:
|
||||
self.instance.extra_credentials.remove(cred)
|
||||
if pk:
|
||||
cred = Credential.objects.get(pk=pk)
|
||||
if cred.credential_type.kind != 'net':
|
||||
raise serializers.ValidationError({
|
||||
'network_credential': _('You must provide a network credential.'),
|
||||
})
|
||||
self.instance.extra_credentials.add(cred)
|
||||
|
||||
if 'project' in self.fields and 'playbook' in self.fields:
|
||||
project = attrs.get('project', self.instance and self.instance.project or None)
|
||||
playbook = attrs.get('playbook', self.instance and self.instance.playbook or '')
|
||||
@@ -2309,10 +2372,6 @@ class JobSerializer(UnifiedJobSerializer, JobOptionsSerializer):
|
||||
data.setdefault('playbook', job_template.playbook)
|
||||
if job_template.credential:
|
||||
data.setdefault('credential', job_template.credential.pk)
|
||||
if job_template.cloud_credential:
|
||||
data.setdefault('cloud_credential', job_template.cloud_credential.pk)
|
||||
if job_template.network_credential:
|
||||
data.setdefault('network_credential', job_template.network_credential.pk)
|
||||
data.setdefault('forks', job_template.forks)
|
||||
data.setdefault('limit', job_template.limit)
|
||||
data.setdefault('verbosity', job_template.verbosity)
|
||||
|
||||
@@ -385,7 +385,9 @@ v1_urls = patterns('awx.api.views',
|
||||
v2_urls = patterns('awx.api.views',
|
||||
url(r'^$', 'api_v2_root_view'),
|
||||
url(r'^credential_types/', include(credential_type_urls)),
|
||||
url(r'^hosts/(?P<pk>[0-9]+)/ansible_facts/$', 'host_ansible_facts_detail'),
|
||||
url(r'^hosts/(?P<pk>[0-9]+)/ansible_facts/$', 'host_ansible_facts_detail'),
|
||||
url(r'^jobs/(?P<pk>[0-9]+)/extra_credentials/$', 'job_extra_credentials_list'),
|
||||
url(r'^job_templates/(?P<pk>[0-9]+)/extra_credentials/$', 'job_template_extra_credentials_list'),
|
||||
)
|
||||
|
||||
urlpatterns = patterns('awx.api.views',
|
||||
|
||||
@@ -2467,7 +2467,7 @@ class JobTemplateList(ListCreateAPIView):
|
||||
always_allow_superuser = False
|
||||
capabilities_prefetch = [
|
||||
'admin', 'execute',
|
||||
{'copy': ['project.use', 'inventory.use', 'credential.use', 'cloud_credential.use', 'network_credential.use']}
|
||||
{'copy': ['project.use', 'inventory.use', 'credential.use']}
|
||||
]
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
@@ -2694,6 +2694,21 @@ class JobTemplateNotificationTemplatesSuccessList(SubListCreateAttachDetachAPIVi
|
||||
new_in_300 = True
|
||||
|
||||
|
||||
class JobTemplateExtraCredentialsList(SubListCreateAttachDetachAPIView):
|
||||
|
||||
model = Credential
|
||||
serializer_class = CredentialSerializer
|
||||
parent_model = JobTemplate
|
||||
relationship = 'extra_credentials'
|
||||
new_in_320 = True
|
||||
new_in_api_v2 = True
|
||||
|
||||
def is_valid_relation(self, parent, sub, created=False):
|
||||
if sub.credential_type.kind not in ('net', 'cloud'):
|
||||
return {'error': _('Extra credentials must be network or cloud.')}
|
||||
return super(JobTemplateExtraCredentialsList, self).is_valid_relation(parent, sub, created)
|
||||
|
||||
|
||||
class JobTemplateLabelList(DeleteLastUnattachLabelMixin, SubListCreateAttachDetachAPIView):
|
||||
|
||||
model = Label
|
||||
@@ -3455,6 +3470,21 @@ class JobDetail(RetrieveUpdateDestroyAPIView):
|
||||
return super(JobDetail, self).destroy(request, *args, **kwargs)
|
||||
|
||||
|
||||
class JobExtraCredentialsList(SubListCreateAttachDetachAPIView):
|
||||
|
||||
model = Credential
|
||||
serializer_class = CredentialSerializer
|
||||
parent_model = Job
|
||||
relationship = 'extra_credentials'
|
||||
new_in_320 = True
|
||||
new_in_api_v2 = True
|
||||
|
||||
def is_valid_relation(self, parent, sub, created=False):
|
||||
if sub.credential_type.kind not in ('net', 'cloud'):
|
||||
return {'error': _('Extra credentials must be network or cloud.')}
|
||||
return super(JobExtraCredentialsList, self).is_valid_relation(parent, sub, created)
|
||||
|
||||
|
||||
class JobLabelList(SubListAPIView):
|
||||
|
||||
model = Label
|
||||
@@ -3941,7 +3971,6 @@ class UnifiedJobTemplateList(ListAPIView):
|
||||
capabilities_prefetch = [
|
||||
'admin', 'execute',
|
||||
{'copy': ['jobtemplate.project.use', 'jobtemplate.inventory.use', 'jobtemplate.credential.use',
|
||||
'jobtemplate.cloud_credential.use', 'jobtemplate.network_credential.use',
|
||||
'workflowjobtemplate.organization.admin']}
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user