mirror of
https://github.com/ansible/awx.git
synced 2026-03-11 14:39:30 -02:30
inventory views
This commit is contained in:
@@ -35,7 +35,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
# Django REST Framework
|
# Django REST Framework
|
||||||
from rest_framework.exceptions import PermissionDenied, ParseError
|
from rest_framework.exceptions import PermissionDenied, ParseError
|
||||||
from rest_framework.parsers import FormParser
|
from rest_framework.parsers import FormParser
|
||||||
from rest_framework.permissions import AllowAny, IsAuthenticated, SAFE_METHODS
|
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.settings import api_settings
|
from rest_framework.settings import api_settings
|
||||||
from rest_framework.views import exception_handler
|
from rest_framework.views import exception_handler
|
||||||
@@ -100,6 +100,7 @@ from awx.api.views.mixin import (
|
|||||||
InstanceGroupMembershipMixin,
|
InstanceGroupMembershipMixin,
|
||||||
RelatedJobsPreventDeleteMixin,
|
RelatedJobsPreventDeleteMixin,
|
||||||
OrganizationCountsMixin,
|
OrganizationCountsMixin,
|
||||||
|
ControlledByScmMixin,
|
||||||
)
|
)
|
||||||
from awx.api.views.organization import ( # noqa
|
from awx.api.views.organization import ( # noqa
|
||||||
OrganizationList,
|
OrganizationList,
|
||||||
@@ -119,6 +120,23 @@ from awx.api.views.organization import ( # noqa
|
|||||||
OrganizationAccessList,
|
OrganizationAccessList,
|
||||||
OrganizationObjectRolesList,
|
OrganizationObjectRolesList,
|
||||||
)
|
)
|
||||||
|
from awx.api.views.inventory import ( # noqa
|
||||||
|
InventoryList,
|
||||||
|
InventoryDetail,
|
||||||
|
InventoryUpdateEventsList,
|
||||||
|
InventoryScriptList,
|
||||||
|
InventoryScriptDetail,
|
||||||
|
InventoryScriptObjectRolesList,
|
||||||
|
InventoryScriptCopy,
|
||||||
|
InventoryList,
|
||||||
|
InventoryDetail,
|
||||||
|
InventoryActivityStreamList,
|
||||||
|
InventoryInstanceGroupsList,
|
||||||
|
InventoryAccessList,
|
||||||
|
InventoryObjectRolesList,
|
||||||
|
InventoryJobTemplateList,
|
||||||
|
InventoryCopy,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger('awx.api.views')
|
logger = logging.getLogger('awx.api.views')
|
||||||
|
|
||||||
@@ -1064,18 +1082,6 @@ class SystemJobEventsList(SubListAPIView):
|
|||||||
return super(SystemJobEventsList, self).finalize_response(request, response, *args, **kwargs)
|
return super(SystemJobEventsList, self).finalize_response(request, response, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class InventoryUpdateEventsList(SubListAPIView):
|
|
||||||
|
|
||||||
model = InventoryUpdateEvent
|
|
||||||
serializer_class = InventoryUpdateEventSerializer
|
|
||||||
parent_model = InventoryUpdate
|
|
||||||
relationship = 'inventory_update_events'
|
|
||||||
view_name = _('Inventory Update Events List')
|
|
||||||
search_fields = ('stdout',)
|
|
||||||
|
|
||||||
def finalize_response(self, request, response, *args, **kwargs):
|
|
||||||
response['X-UI-Max-Events'] = settings.MAX_UI_JOB_EVENTS
|
|
||||||
return super(InventoryUpdateEventsList, self).finalize_response(request, response, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class ProjectUpdateCancel(RetrieveAPIView):
|
class ProjectUpdateCancel(RetrieveAPIView):
|
||||||
@@ -1633,177 +1639,6 @@ class CredentialCopy(CopyAPIView):
|
|||||||
copy_return_serializer_class = CredentialSerializer
|
copy_return_serializer_class = CredentialSerializer
|
||||||
|
|
||||||
|
|
||||||
class InventoryScriptList(ListCreateAPIView):
|
|
||||||
|
|
||||||
model = CustomInventoryScript
|
|
||||||
serializer_class = CustomInventoryScriptSerializer
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryScriptDetail(RetrieveUpdateDestroyAPIView):
|
|
||||||
|
|
||||||
model = CustomInventoryScript
|
|
||||||
serializer_class = CustomInventoryScriptSerializer
|
|
||||||
|
|
||||||
def destroy(self, request, *args, **kwargs):
|
|
||||||
instance = self.get_object()
|
|
||||||
can_delete = request.user.can_access(self.model, 'delete', instance)
|
|
||||||
if not can_delete:
|
|
||||||
raise PermissionDenied(_("Cannot delete inventory script."))
|
|
||||||
for inv_src in InventorySource.objects.filter(source_script=instance):
|
|
||||||
inv_src.source_script = None
|
|
||||||
inv_src.save()
|
|
||||||
return super(InventoryScriptDetail, self).destroy(request, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryScriptObjectRolesList(SubListAPIView):
|
|
||||||
|
|
||||||
model = Role
|
|
||||||
serializer_class = RoleSerializer
|
|
||||||
parent_model = CustomInventoryScript
|
|
||||||
search_fields = ('role_field', 'content_type__model',)
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
po = self.get_parent_object()
|
|
||||||
content_type = ContentType.objects.get_for_model(self.parent_model)
|
|
||||||
return Role.objects.filter(content_type=content_type, object_id=po.pk)
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryScriptCopy(CopyAPIView):
|
|
||||||
|
|
||||||
model = CustomInventoryScript
|
|
||||||
copy_return_serializer_class = CustomInventoryScriptSerializer
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryList(ListCreateAPIView):
|
|
||||||
|
|
||||||
model = Inventory
|
|
||||||
serializer_class = InventorySerializer
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
qs = Inventory.accessible_objects(self.request.user, 'read_role')
|
|
||||||
qs = qs.select_related('admin_role', 'read_role', 'update_role', 'use_role', 'adhoc_role')
|
|
||||||
qs = qs.prefetch_related('created_by', 'modified_by', 'organization')
|
|
||||||
return qs
|
|
||||||
|
|
||||||
|
|
||||||
class ControlledByScmMixin(object):
|
|
||||||
'''
|
|
||||||
Special method to reset SCM inventory commit hash
|
|
||||||
if anything that it manages changes.
|
|
||||||
'''
|
|
||||||
|
|
||||||
def _reset_inv_src_rev(self, obj):
|
|
||||||
if self.request.method in SAFE_METHODS or not obj:
|
|
||||||
return
|
|
||||||
project_following_sources = obj.inventory_sources.filter(
|
|
||||||
update_on_project_update=True, source='scm')
|
|
||||||
if project_following_sources:
|
|
||||||
# Allow inventory changes unrelated to variables
|
|
||||||
if self.model == Inventory and (
|
|
||||||
not self.request or not self.request.data or
|
|
||||||
parse_yaml_or_json(self.request.data.get('variables', '')) == parse_yaml_or_json(obj.variables)):
|
|
||||||
return
|
|
||||||
project_following_sources.update(scm_last_revision='')
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
obj = super(ControlledByScmMixin, self).get_object()
|
|
||||||
self._reset_inv_src_rev(obj)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
def get_parent_object(self):
|
|
||||||
obj = super(ControlledByScmMixin, self).get_parent_object()
|
|
||||||
self._reset_inv_src_rev(obj)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryDetail(RelatedJobsPreventDeleteMixin, ControlledByScmMixin, RetrieveUpdateDestroyAPIView):
|
|
||||||
|
|
||||||
model = Inventory
|
|
||||||
serializer_class = InventoryDetailSerializer
|
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
|
||||||
obj = self.get_object()
|
|
||||||
kind = self.request.data.get('kind') or kwargs.get('kind')
|
|
||||||
|
|
||||||
# Do not allow changes to an Inventory kind.
|
|
||||||
if kind is not None and obj.kind != kind:
|
|
||||||
return self.http_method_not_allowed(request, *args, **kwargs)
|
|
||||||
return super(InventoryDetail, self).update(request, *args, **kwargs)
|
|
||||||
|
|
||||||
def destroy(self, request, *args, **kwargs):
|
|
||||||
obj = self.get_object()
|
|
||||||
if not request.user.can_access(self.model, 'delete', obj):
|
|
||||||
raise PermissionDenied()
|
|
||||||
self.check_related_active_jobs(obj) # related jobs mixin
|
|
||||||
try:
|
|
||||||
obj.schedule_deletion(getattr(request.user, 'id', None))
|
|
||||||
return Response(status=status.HTTP_202_ACCEPTED)
|
|
||||||
except RuntimeError as e:
|
|
||||||
return Response(dict(error=_("{0}".format(e))), status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryActivityStreamList(ActivityStreamEnforcementMixin, SubListAPIView):
|
|
||||||
|
|
||||||
model = ActivityStream
|
|
||||||
serializer_class = ActivityStreamSerializer
|
|
||||||
parent_model = Inventory
|
|
||||||
relationship = 'activitystream_set'
|
|
||||||
search_fields = ('changes',)
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
parent = self.get_parent_object()
|
|
||||||
self.check_parent_access(parent)
|
|
||||||
qs = self.request.user.get_queryset(self.model)
|
|
||||||
return qs.filter(Q(inventory=parent) | Q(host__in=parent.hosts.all()) | Q(group__in=parent.groups.all()))
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryInstanceGroupsList(SubListAttachDetachAPIView):
|
|
||||||
|
|
||||||
model = InstanceGroup
|
|
||||||
serializer_class = InstanceGroupSerializer
|
|
||||||
parent_model = Inventory
|
|
||||||
relationship = 'instance_groups'
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryAccessList(ResourceAccessList):
|
|
||||||
|
|
||||||
model = User # needs to be User for AccessLists's
|
|
||||||
parent_model = Inventory
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryObjectRolesList(SubListAPIView):
|
|
||||||
|
|
||||||
model = Role
|
|
||||||
serializer_class = RoleSerializer
|
|
||||||
parent_model = Inventory
|
|
||||||
search_fields = ('role_field', 'content_type__model',)
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
po = self.get_parent_object()
|
|
||||||
content_type = ContentType.objects.get_for_model(self.parent_model)
|
|
||||||
return Role.objects.filter(content_type=content_type, object_id=po.pk)
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryJobTemplateList(SubListAPIView):
|
|
||||||
|
|
||||||
model = JobTemplate
|
|
||||||
serializer_class = JobTemplateSerializer
|
|
||||||
parent_model = Inventory
|
|
||||||
relationship = 'jobtemplates'
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
parent = self.get_parent_object()
|
|
||||||
self.check_parent_access(parent)
|
|
||||||
qs = self.request.user.get_queryset(self.model)
|
|
||||||
return qs.filter(inventory=parent)
|
|
||||||
|
|
||||||
|
|
||||||
class InventoryCopy(CopyAPIView):
|
|
||||||
|
|
||||||
model = Inventory
|
|
||||||
copy_return_serializer_class = InventorySerializer
|
|
||||||
|
|
||||||
|
|
||||||
class HostRelatedSearchMixin(object):
|
class HostRelatedSearchMixin(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
211
awx/api/views/inventory.py
Normal file
211
awx/api/views/inventory.py
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
# Copyright (c) 2018 Red Hat, Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
|
||||||
|
# Python
|
||||||
|
import logging
|
||||||
|
|
||||||
|
# Django
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db.models import Q
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
# Django REST Framework
|
||||||
|
from rest_framework.exceptions import PermissionDenied
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
# AWX
|
||||||
|
from awx.main.models import (
|
||||||
|
ActivityStream,
|
||||||
|
Inventory,
|
||||||
|
JobTemplate,
|
||||||
|
Role,
|
||||||
|
User,
|
||||||
|
InstanceGroup,
|
||||||
|
InventoryUpdateEvent,
|
||||||
|
InventoryUpdate,
|
||||||
|
InventorySource,
|
||||||
|
CustomInventoryScript,
|
||||||
|
)
|
||||||
|
from awx.api.generics import (
|
||||||
|
ListCreateAPIView,
|
||||||
|
RetrieveUpdateDestroyAPIView,
|
||||||
|
SubListAPIView,
|
||||||
|
SubListAttachDetachAPIView,
|
||||||
|
ResourceAccessList,
|
||||||
|
CopyAPIView,
|
||||||
|
)
|
||||||
|
|
||||||
|
from awx.api.serializers import (
|
||||||
|
InventorySerializer,
|
||||||
|
ActivityStreamSerializer,
|
||||||
|
RoleSerializer,
|
||||||
|
InstanceGroupSerializer,
|
||||||
|
InventoryUpdateEventSerializer,
|
||||||
|
CustomInventoryScriptSerializer,
|
||||||
|
InventoryDetailSerializer,
|
||||||
|
JobTemplateSerializer,
|
||||||
|
)
|
||||||
|
from awx.api.views.mixin import (
|
||||||
|
ActivityStreamEnforcementMixin,
|
||||||
|
RelatedJobsPreventDeleteMixin,
|
||||||
|
ControlledByScmMixin,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger('awx.api.views.organization')
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryUpdateEventsList(SubListAPIView):
|
||||||
|
|
||||||
|
model = InventoryUpdateEvent
|
||||||
|
serializer_class = InventoryUpdateEventSerializer
|
||||||
|
parent_model = InventoryUpdate
|
||||||
|
relationship = 'inventory_update_events'
|
||||||
|
view_name = _('Inventory Update Events List')
|
||||||
|
search_fields = ('stdout',)
|
||||||
|
|
||||||
|
def finalize_response(self, request, response, *args, **kwargs):
|
||||||
|
response['X-UI-Max-Events'] = settings.MAX_UI_JOB_EVENTS
|
||||||
|
return super(InventoryUpdateEventsList, self).finalize_response(request, response, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryScriptList(ListCreateAPIView):
|
||||||
|
|
||||||
|
model = CustomInventoryScript
|
||||||
|
serializer_class = CustomInventoryScriptSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryScriptDetail(RetrieveUpdateDestroyAPIView):
|
||||||
|
|
||||||
|
model = CustomInventoryScript
|
||||||
|
serializer_class = CustomInventoryScriptSerializer
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
instance = self.get_object()
|
||||||
|
can_delete = request.user.can_access(self.model, 'delete', instance)
|
||||||
|
if not can_delete:
|
||||||
|
raise PermissionDenied(_("Cannot delete inventory script."))
|
||||||
|
for inv_src in InventorySource.objects.filter(source_script=instance):
|
||||||
|
inv_src.source_script = None
|
||||||
|
inv_src.save()
|
||||||
|
return super(InventoryScriptDetail, self).destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryScriptObjectRolesList(SubListAPIView):
|
||||||
|
|
||||||
|
model = Role
|
||||||
|
serializer_class = RoleSerializer
|
||||||
|
parent_model = CustomInventoryScript
|
||||||
|
search_fields = ('role_field', 'content_type__model',)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
po = self.get_parent_object()
|
||||||
|
content_type = ContentType.objects.get_for_model(self.parent_model)
|
||||||
|
return Role.objects.filter(content_type=content_type, object_id=po.pk)
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryScriptCopy(CopyAPIView):
|
||||||
|
|
||||||
|
model = CustomInventoryScript
|
||||||
|
copy_return_serializer_class = CustomInventoryScriptSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryList(ListCreateAPIView):
|
||||||
|
|
||||||
|
model = Inventory
|
||||||
|
serializer_class = InventorySerializer
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
qs = Inventory.accessible_objects(self.request.user, 'read_role')
|
||||||
|
qs = qs.select_related('admin_role', 'read_role', 'update_role', 'use_role', 'adhoc_role')
|
||||||
|
qs = qs.prefetch_related('created_by', 'modified_by', 'organization')
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryDetail(RelatedJobsPreventDeleteMixin, ControlledByScmMixin, RetrieveUpdateDestroyAPIView):
|
||||||
|
|
||||||
|
model = Inventory
|
||||||
|
serializer_class = InventoryDetailSerializer
|
||||||
|
|
||||||
|
def update(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
kind = self.request.data.get('kind') or kwargs.get('kind')
|
||||||
|
|
||||||
|
# Do not allow changes to an Inventory kind.
|
||||||
|
if kind is not None and obj.kind != kind:
|
||||||
|
return self.http_method_not_allowed(request, *args, **kwargs)
|
||||||
|
return super(InventoryDetail, self).update(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
obj = self.get_object()
|
||||||
|
if not request.user.can_access(self.model, 'delete', obj):
|
||||||
|
raise PermissionDenied()
|
||||||
|
self.check_related_active_jobs(obj) # related jobs mixin
|
||||||
|
try:
|
||||||
|
obj.schedule_deletion(getattr(request.user, 'id', None))
|
||||||
|
return Response(status=status.HTTP_202_ACCEPTED)
|
||||||
|
except RuntimeError as e:
|
||||||
|
return Response(dict(error=_("{0}".format(e))), status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryActivityStreamList(ActivityStreamEnforcementMixin, SubListAPIView):
|
||||||
|
|
||||||
|
model = ActivityStream
|
||||||
|
serializer_class = ActivityStreamSerializer
|
||||||
|
parent_model = Inventory
|
||||||
|
relationship = 'activitystream_set'
|
||||||
|
search_fields = ('changes',)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
parent = self.get_parent_object()
|
||||||
|
self.check_parent_access(parent)
|
||||||
|
qs = self.request.user.get_queryset(self.model)
|
||||||
|
return qs.filter(Q(inventory=parent) | Q(host__in=parent.hosts.all()) | Q(group__in=parent.groups.all()))
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryInstanceGroupsList(SubListAttachDetachAPIView):
|
||||||
|
|
||||||
|
model = InstanceGroup
|
||||||
|
serializer_class = InstanceGroupSerializer
|
||||||
|
parent_model = Inventory
|
||||||
|
relationship = 'instance_groups'
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryAccessList(ResourceAccessList):
|
||||||
|
|
||||||
|
model = User # needs to be User for AccessLists's
|
||||||
|
parent_model = Inventory
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryObjectRolesList(SubListAPIView):
|
||||||
|
|
||||||
|
model = Role
|
||||||
|
serializer_class = RoleSerializer
|
||||||
|
parent_model = Inventory
|
||||||
|
search_fields = ('role_field', 'content_type__model',)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
po = self.get_parent_object()
|
||||||
|
content_type = ContentType.objects.get_for_model(self.parent_model)
|
||||||
|
return Role.objects.filter(content_type=content_type, object_id=po.pk)
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryJobTemplateList(SubListAPIView):
|
||||||
|
|
||||||
|
model = JobTemplate
|
||||||
|
serializer_class = JobTemplateSerializer
|
||||||
|
parent_model = Inventory
|
||||||
|
relationship = 'jobtemplates'
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
parent = self.get_parent_object()
|
||||||
|
self.check_parent_access(parent)
|
||||||
|
qs = self.request.user.get_queryset(self.model)
|
||||||
|
return qs.filter(inventory=parent)
|
||||||
|
|
||||||
|
|
||||||
|
class InventoryCopy(CopyAPIView):
|
||||||
|
|
||||||
|
model = Inventory
|
||||||
|
copy_return_serializer_class = InventorySerializer
|
||||||
@@ -13,12 +13,16 @@ from django.shortcuts import get_object_or_404
|
|||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from rest_framework.permissions import SAFE_METHODS
|
||||||
from rest_framework.exceptions import PermissionDenied
|
from rest_framework.exceptions import PermissionDenied
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
|
||||||
from awx.main.constants import ACTIVE_STATES
|
from awx.main.constants import ACTIVE_STATES
|
||||||
from awx.main.utils import get_object_or_400
|
from awx.main.utils import (
|
||||||
|
get_object_or_400,
|
||||||
|
parse_yaml_or_json,
|
||||||
|
)
|
||||||
from awx.main.models.ha import (
|
from awx.main.models.ha import (
|
||||||
Instance,
|
Instance,
|
||||||
InstanceGroup,
|
InstanceGroup,
|
||||||
@@ -273,3 +277,33 @@ class OrganizationCountsMixin(object):
|
|||||||
full_context['related_field_counts'] = count_context
|
full_context['related_field_counts'] = count_context
|
||||||
|
|
||||||
return full_context
|
return full_context
|
||||||
|
|
||||||
|
|
||||||
|
class ControlledByScmMixin(object):
|
||||||
|
'''
|
||||||
|
Special method to reset SCM inventory commit hash
|
||||||
|
if anything that it manages changes.
|
||||||
|
'''
|
||||||
|
|
||||||
|
def _reset_inv_src_rev(self, obj):
|
||||||
|
if self.request.method in SAFE_METHODS or not obj:
|
||||||
|
return
|
||||||
|
project_following_sources = obj.inventory_sources.filter(
|
||||||
|
update_on_project_update=True, source='scm')
|
||||||
|
if project_following_sources:
|
||||||
|
# Allow inventory changes unrelated to variables
|
||||||
|
if self.model == Inventory and (
|
||||||
|
not self.request or not self.request.data or
|
||||||
|
parse_yaml_or_json(self.request.data.get('variables', '')) == parse_yaml_or_json(obj.variables)):
|
||||||
|
return
|
||||||
|
project_following_sources.update(scm_last_revision='')
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
obj = super(ControlledByScmMixin, self).get_object()
|
||||||
|
self._reset_inv_src_rev(obj)
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def get_parent_object(self):
|
||||||
|
obj = super(ControlledByScmMixin, self).get_parent_object()
|
||||||
|
self._reset_inv_src_rev(obj)
|
||||||
|
return obj
|
||||||
|
|||||||
Reference in New Issue
Block a user