mirror of
https://github.com/ansible/awx.git
synced 2026-01-12 18:40:01 -03:30
Merge pull request #6029 from AlanCoding/group_v1
Group serializer special v1 functionality
This commit is contained in:
commit
14a03d2cb5
@ -1238,13 +1238,41 @@ class HostSerializer(BaseSerializerWithVariables):
|
||||
|
||||
|
||||
class GroupSerializer(BaseSerializerWithVariables):
|
||||
show_capabilities = ['copy', 'edit', 'delete']
|
||||
inventory_source = serializers.SerializerMethodField(
|
||||
help_text=_('Dedicated inventory source for the group, will be removed in 3.3.'))
|
||||
|
||||
class Meta:
|
||||
model = Group
|
||||
fields = ('*', 'inventory', 'variables', 'has_active_failures',
|
||||
'total_hosts', 'hosts_with_active_failures', 'total_groups',
|
||||
'groups_with_active_failures', 'has_inventory_sources')
|
||||
'groups_with_active_failures', 'has_inventory_sources', 'inventory_source')
|
||||
|
||||
def get_fields(self): # TODO: remove in 3.3
|
||||
fields = super(GroupSerializer, self).get_fields()
|
||||
if not self.V1:
|
||||
fields.pop('inventory_source')
|
||||
return fields
|
||||
|
||||
@property
|
||||
def V1(self):
|
||||
request = self.context.get('request')
|
||||
# TODO: use the better version-getter after merged with other branches
|
||||
if request and request.version == 'v1':
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def show_capabilities(self): # TODO: consolidate in 3.3
|
||||
if self.V1:
|
||||
return ['copy', 'edit', 'start', 'schedule', 'delete']
|
||||
else:
|
||||
return ['copy', 'edit', 'delete']
|
||||
|
||||
def get_inventory_source(self, obj): # TODO: remove in 3.3
|
||||
try:
|
||||
return obj.deprecated_inventory_source.id
|
||||
except Group.deprecated_inventory_source.RelatedObjectDoesNotExist:
|
||||
return None
|
||||
|
||||
def build_relational_field(self, field_name, relation_info):
|
||||
field_class, field_kwargs = super(GroupSerializer, self).build_relational_field(field_name, relation_info)
|
||||
@ -1268,10 +1296,22 @@ class GroupSerializer(BaseSerializerWithVariables):
|
||||
inventory_sources = self.reverse('api:group_inventory_sources_list', kwargs={'pk': obj.pk}),
|
||||
ad_hoc_commands = self.reverse('api:group_ad_hoc_commands_list', kwargs={'pk': obj.pk}),
|
||||
))
|
||||
if self.V1: # TODO: remove in 3.3
|
||||
try:
|
||||
res['inventory_source'] = self.reverse('api:inventory_source_detail',
|
||||
kwargs={'pk': obj.deprecated_inventory_source.pk})
|
||||
except Group.deprecated_inventory_source.RelatedObjectDoesNotExist:
|
||||
res['inventory_source'] = None
|
||||
if obj.inventory:
|
||||
res['inventory'] = self.reverse('api:inventory_detail', kwargs={'pk': obj.inventory.pk})
|
||||
return res
|
||||
|
||||
def create(self, validated_data): # TODO: remove in 3.3
|
||||
instance = super(GroupSerializer, self).create(validated_data)
|
||||
if self.V1:
|
||||
InventorySource.objects.create(deprecated_group=instance, inventory=instance.inventory)
|
||||
return instance
|
||||
|
||||
def validate_name(self, value):
|
||||
if value in ('all', '_meta'):
|
||||
raise serializers.ValidationError(_('Invalid group name.'))
|
||||
@ -1428,11 +1468,13 @@ class InventorySourceSerializer(UnifiedJobTemplateSerializer, InventorySourceOpt
|
||||
last_update_failed = serializers.BooleanField(read_only=True)
|
||||
last_updated = serializers.DateTimeField(read_only=True)
|
||||
show_capabilities = ['start', 'schedule', 'edit', 'delete']
|
||||
group = serializers.SerializerMethodField(
|
||||
help_text=_('Automatic group relationship, will be removed in 3.3'))
|
||||
|
||||
class Meta:
|
||||
model = InventorySource
|
||||
fields = ('*', 'name', 'inventory', 'update_on_launch', 'update_cache_timeout') + \
|
||||
('last_update_failed', 'last_updated') # Backwards compatibility.
|
||||
('last_update_failed', 'last_updated', 'group') # Backwards compatibility.
|
||||
|
||||
def get_related(self, obj):
|
||||
res = super(InventorySourceSerializer, self).get_related(obj)
|
||||
@ -1456,8 +1498,30 @@ class InventorySourceSerializer(UnifiedJobTemplateSerializer, InventorySourceOpt
|
||||
if obj.last_update:
|
||||
res['last_update'] = self.reverse('api:inventory_update_detail',
|
||||
kwargs={'pk': obj.last_update.pk})
|
||||
if self.V1: # TODO: remove in 3.3
|
||||
res['group'] = None
|
||||
if obj.deprecated_group:
|
||||
res['group'] = self.reverse('api:group_detail', kwargs={'pk': obj.deprecated_group.pk})
|
||||
return res
|
||||
|
||||
def get_fields(self): # TODO: remove in 3.3
|
||||
fields = super(InventorySourceSerializer, self).get_fields()
|
||||
if not self.V1:
|
||||
fields.pop('group')
|
||||
return fields
|
||||
|
||||
def get_group(self, obj): # TODO: remove in 3.3
|
||||
if obj.deprecated_group:
|
||||
return obj.deprecated_group.id
|
||||
return None
|
||||
|
||||
@property
|
||||
def V1(self): # TODO: use the better version-getter after merged with other branches
|
||||
request = self.context.get('request')
|
||||
if request and request.version == 'v1':
|
||||
return True
|
||||
return False
|
||||
|
||||
def to_representation(self, obj):
|
||||
ret = super(InventorySourceSerializer, self).to_representation(obj)
|
||||
if obj is None:
|
||||
|
||||
@ -1855,7 +1855,7 @@ class GroupList(ListCreateAPIView):
|
||||
|
||||
model = Group
|
||||
serializer_class = GroupSerializer
|
||||
capabilities_prefetch = ['inventory.admin', 'inventory.adhoc', 'inventory.update']
|
||||
capabilities_prefetch = ['inventory.admin', 'inventory.adhoc']
|
||||
|
||||
|
||||
class EnforceParentRelationshipMixin(object):
|
||||
@ -1999,6 +1999,11 @@ class GroupDetail(RetrieveUpdateDestroyAPIView):
|
||||
obj = self.get_object()
|
||||
if not request.user.can_access(self.model, 'delete', obj):
|
||||
raise PermissionDenied()
|
||||
if self.request.version == 'v1': # TODO: deletion of automatic inventory_source, remove in 3.3
|
||||
try:
|
||||
obj.deprecated_inventory_source.delete()
|
||||
except Group.deprecated_inventory_source.RelatedObjectDoesNotExist:
|
||||
pass
|
||||
obj.delete_recursive()
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
@ -2189,7 +2194,7 @@ class InventorySourceList(ListAPIView):
|
||||
new_in_14 = True
|
||||
|
||||
|
||||
class InventorySourceDetail(RetrieveUpdateAPIView):
|
||||
class InventorySourceDetail(RetrieveUpdateDestroyAPIView):
|
||||
|
||||
model = InventorySource
|
||||
serializer_class = InventorySourceSerializer
|
||||
|
||||
@ -346,6 +346,17 @@ class BaseAccess(object):
|
||||
elif display_method == 'copy' and isinstance(obj, WorkflowJobTemplate) and obj.organization_id is None:
|
||||
user_capabilities[display_method] = self.user.is_superuser
|
||||
continue
|
||||
elif display_method in ['start', 'schedule'] and isinstance(obj, Group): # TODO: remove in 3.3
|
||||
try:
|
||||
if obj.deprecated_inventory_source and not obj.deprecated_inventory_source._can_update():
|
||||
user_capabilities[display_method] = False
|
||||
continue
|
||||
except Group.deprecated_inventory_source.RelatedObjectDoesNotExist:
|
||||
user_capabilities[display_method] = False
|
||||
continue
|
||||
if obj.inventory_source and not obj.inventory_source._can_update():
|
||||
user_capabilities[display_method] = False
|
||||
continue
|
||||
elif display_method in ['start', 'schedule'] and isinstance(obj, (Project)):
|
||||
if obj.scm_type == '':
|
||||
user_capabilities[display_method] = False
|
||||
@ -720,6 +731,19 @@ class GroupAccess(BaseAccess):
|
||||
"active_jobs": active_jobs})
|
||||
return True
|
||||
|
||||
def can_start(self, obj, validate_license=True):
|
||||
# TODO: Delete for 3.3, only used by v1 serializer
|
||||
# Used as another alias to inventory_source start access for user_capabilities
|
||||
if obj:
|
||||
try:
|
||||
return self.user.can_access(
|
||||
InventorySource, 'start', obj.deprecated_inventory_source,
|
||||
validate_license=validate_license)
|
||||
obj.deprecated_inventory_source
|
||||
except Group.deprecated_inventory_source.RelatedObjectDoesNotExist:
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
class InventorySourceAccess(BaseAccess):
|
||||
'''
|
||||
|
||||
@ -28,7 +28,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='inventorysource',
|
||||
name='deprecated_group',
|
||||
field=models.ForeignKey(related_name='deprecated_inventory_source', default=None, null=True, to='main.Group'),
|
||||
field=models.OneToOneField(related_name='deprecated_inventory_source', null=True, default=None, to='main.Group'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='inventorysource',
|
||||
|
||||
@ -19,7 +19,6 @@ class Migration(migrations.Migration):
|
||||
operations = [
|
||||
# Inventory Refresh
|
||||
migrations.RunPython(migration_utils.set_current_apps_for_migrations),
|
||||
migrations.RunPython(invsrc.remove_manual_inventory_sources),
|
||||
migrations.RunPython(invsrc.remove_inventory_source_with_no_inventory_link),
|
||||
migrations.RunPython(invsrc.rename_inventory_sources),
|
||||
]
|
||||
|
||||
@ -10,6 +10,7 @@ def remove_manual_inventory_sources(apps, schema_editor):
|
||||
Group creation and we would use the parent Group as our interface for the user.
|
||||
During that process we would create InventorySource that had a source of "manual".
|
||||
'''
|
||||
# TODO: use this in the 3.3 data migrations
|
||||
InventorySource = apps.get_model('main', 'InventorySource')
|
||||
# see models/inventory.py SOURCE_CHOICES - ('', _('Manual'))
|
||||
logger.debug("Removing all Manual InventorySource from database.")
|
||||
|
||||
@ -1063,7 +1063,7 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions):
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
deprecated_group = models.ForeignKey(
|
||||
deprecated_group = models.OneToOneField(
|
||||
'Group',
|
||||
related_name='deprecated_inventory_source',
|
||||
null=True,
|
||||
@ -1178,6 +1178,16 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions):
|
||||
success=list(success_notification_templates),
|
||||
any=list(any_notification_templates))
|
||||
|
||||
def clean_source(self): # TODO: remove in 3.3
|
||||
source = self.source
|
||||
if source and self.deprecated_group:
|
||||
qs = self.deprecated_group.inventory_sources.filter(source__in=CLOUD_INVENTORY_SOURCES)
|
||||
existing_sources = qs.exclude(pk=self.pk)
|
||||
if existing_sources.count():
|
||||
s = u', '.join([x.deprecated_group.name for x in existing_sources])
|
||||
raise ValidationError(_('Unable to configure this item for cloud sync. It is already managed by %s.') % s)
|
||||
return source
|
||||
|
||||
|
||||
class InventoryUpdate(UnifiedJob, InventorySourceOptions, JobNotificationMixin):
|
||||
'''
|
||||
@ -1212,6 +1222,8 @@ class InventoryUpdate(UnifiedJob, InventorySourceOptions, JobNotificationMixin):
|
||||
|
||||
def websocket_emit_data(self):
|
||||
websocket_data = super(InventoryUpdate, self).websocket_emit_data()
|
||||
if self.inventory_source.deprecated_group is not None: # TODO: remove in 3.3
|
||||
websocket_data.update(dict(group_id=self.inventory_source.deprecated_group.id))
|
||||
return websocket_data
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
@ -372,6 +372,10 @@ model_serializer_mapping = {
|
||||
|
||||
def activity_stream_create(sender, instance, created, **kwargs):
|
||||
if created and activity_stream_enabled:
|
||||
# TODO: remove deprecated_group conditional in 3.3
|
||||
# Skip recording any inventory source directly associated with a group.
|
||||
if isinstance(instance, InventorySource) and instance.deprecated_group:
|
||||
return
|
||||
object1 = camelcase_to_underscore(instance.__class__.__name__)
|
||||
changes = model_to_dict(instance, model_serializer_mapping)
|
||||
# Special case where Job survey password variables need to be hidden
|
||||
@ -417,6 +421,10 @@ def activity_stream_update(sender, instance, **kwargs):
|
||||
def activity_stream_delete(sender, instance, **kwargs):
|
||||
if not activity_stream_enabled:
|
||||
return
|
||||
# TODO: remove deprecated_group conditional in 3.3
|
||||
# Skip recording any inventory source directly associated with a group.
|
||||
if isinstance(instance, InventorySource) and instance.deprecated_group:
|
||||
return
|
||||
changes = model_to_dict(instance)
|
||||
object1 = camelcase_to_underscore(instance.__class__.__name__)
|
||||
activity_entry = ActivityStream(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user