mirror of
https://github.com/ansible/awx.git
synced 2026-05-17 14:27:42 -02:30
237
awx/api/views.py
237
awx/api/views.py
@@ -30,6 +30,7 @@ from django.template.loader import render_to_string
|
||||
from django.core.servers.basehttp import FileWrapper
|
||||
from django.http import HttpResponse
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
# Django REST Framework
|
||||
@@ -88,14 +89,14 @@ class ApiRootView(APIView):
|
||||
|
||||
authentication_classes = []
|
||||
permission_classes = (AllowAny,)
|
||||
view_name = 'REST API'
|
||||
view_name = _('REST API')
|
||||
|
||||
def get(self, request, format=None):
|
||||
''' list supported API versions '''
|
||||
|
||||
current = reverse('api:api_v1_root_view', args=[])
|
||||
data = dict(
|
||||
description = 'Ansible Tower REST API',
|
||||
description = _('Ansible Tower REST API'),
|
||||
current_version = current,
|
||||
available_versions = dict(
|
||||
v1 = current
|
||||
@@ -107,7 +108,7 @@ class ApiV1RootView(APIView):
|
||||
|
||||
authentication_classes = []
|
||||
permission_classes = (AllowAny,)
|
||||
view_name = 'Version 1'
|
||||
view_name = _('Version 1')
|
||||
|
||||
def get(self, request, format=None):
|
||||
''' list top level resources '''
|
||||
@@ -158,7 +159,7 @@ class ApiV1PingView(APIView):
|
||||
"""
|
||||
permission_classes = (AllowAny,)
|
||||
authentication_classes = ()
|
||||
view_name = 'Ping'
|
||||
view_name = _('Ping')
|
||||
new_in_210 = True
|
||||
|
||||
def get(self, request, format=None):
|
||||
@@ -185,7 +186,7 @@ class ApiV1PingView(APIView):
|
||||
class ApiV1ConfigView(APIView):
|
||||
|
||||
permission_classes = (IsAuthenticated,)
|
||||
view_name = 'Configuration'
|
||||
view_name = _('Configuration')
|
||||
|
||||
def get(self, request, format=None):
|
||||
'''Return various sitewide configuration settings.'''
|
||||
@@ -235,29 +236,29 @@ class ApiV1ConfigView(APIView):
|
||||
if not request.user.is_superuser:
|
||||
return Response(None, status=status.HTTP_404_NOT_FOUND)
|
||||
if not isinstance(request.data, dict):
|
||||
return Response({"error": "Invalid license data"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response({"error": _("Invalid license data")}, status=status.HTTP_400_BAD_REQUEST)
|
||||
if "eula_accepted" not in request.data:
|
||||
return Response({"error": "Missing 'eula_accepted' property"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response({"error": _("Missing 'eula_accepted' property")}, status=status.HTTP_400_BAD_REQUEST)
|
||||
try:
|
||||
eula_accepted = to_python_boolean(request.data["eula_accepted"])
|
||||
except ValueError:
|
||||
return Response({"error": "'eula_accepted' value is invalid"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response({"error": _("'eula_accepted' value is invalid")}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if not eula_accepted:
|
||||
return Response({"error": "'eula_accepted' must be True"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response({"error": _("'eula_accepted' must be True")}, status=status.HTTP_400_BAD_REQUEST)
|
||||
request.data.pop("eula_accepted")
|
||||
try:
|
||||
data_actual = json.dumps(request.data)
|
||||
except Exception:
|
||||
# FIX: Log
|
||||
return Response({"error": "Invalid JSON"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response({"error": _("Invalid JSON")}, status=status.HTTP_400_BAD_REQUEST)
|
||||
try:
|
||||
from awx.main.task_engine import TaskEnhancer
|
||||
license_data = json.loads(data_actual)
|
||||
license_data_validated = TaskEnhancer(**license_data).validate_enhancements()
|
||||
except Exception:
|
||||
# FIX: Log
|
||||
return Response({"error": "Invalid License"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response({"error": _("Invalid License")}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# If the license is valid, write it to the database.
|
||||
if license_data_validated['valid_key']:
|
||||
@@ -265,7 +266,7 @@ class ApiV1ConfigView(APIView):
|
||||
settings.TOWER_URL_BASE = "{}://{}".format(request.scheme, request.get_host())
|
||||
return Response(license_data_validated)
|
||||
|
||||
return Response({"error": "Invalid license"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response({"error": _("Invalid license")}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def delete(self, request):
|
||||
if not request.user.is_superuser:
|
||||
@@ -276,12 +277,12 @@ class ApiV1ConfigView(APIView):
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
except:
|
||||
# FIX: Log
|
||||
return Response({"error": "Failed to remove license (%s)" % has_error}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response({"error": _("Failed to remove license (%s)") % has_error}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class DashboardView(APIView):
|
||||
|
||||
view_name = "Dashboard"
|
||||
view_name = _("Dashboard")
|
||||
new_in_14 = True
|
||||
|
||||
def get(self, request, format=None):
|
||||
@@ -386,7 +387,7 @@ class DashboardView(APIView):
|
||||
|
||||
class DashboardJobsGraphView(APIView):
|
||||
|
||||
view_name = "Dashboard Jobs Graphs"
|
||||
view_name = _("Dashboard Jobs Graphs")
|
||||
new_in_200 = True
|
||||
|
||||
def get(self, request, format=None):
|
||||
@@ -422,7 +423,7 @@ class DashboardJobsGraphView(APIView):
|
||||
end_date = start_date - dateutil.relativedelta.relativedelta(days=1)
|
||||
interval = 'hours'
|
||||
else:
|
||||
return Response({'error': 'Unknown period "%s"' % str(period)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response({'error': _('Unknown period "%s"') % str(period)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
dashboard_data = {"jobs": {"successful": [], "failed": []}}
|
||||
for element in success_qss.time_series(end_date, start_date, interval=interval):
|
||||
@@ -436,7 +437,7 @@ class DashboardJobsGraphView(APIView):
|
||||
|
||||
class ScheduleList(ListAPIView):
|
||||
|
||||
view_name = "Schedules"
|
||||
view_name = _("Schedules")
|
||||
model = Schedule
|
||||
serializer_class = ScheduleSerializer
|
||||
new_in_148 = True
|
||||
@@ -453,7 +454,7 @@ class ScheduleUnifiedJobsList(SubListAPIView):
|
||||
serializer_class = UnifiedJobSerializer
|
||||
parent_model = Schedule
|
||||
relationship = 'unifiedjob_set'
|
||||
view_name = 'Schedule Jobs List'
|
||||
view_name = _('Schedule Jobs List')
|
||||
new_in_148 = True
|
||||
|
||||
class AuthView(APIView):
|
||||
@@ -655,8 +656,8 @@ class OrganizationList(OrganizationCountsMixin, ListCreateAPIView):
|
||||
# if no organizations exist in the system.
|
||||
if (not feature_enabled('multiple_organizations') and
|
||||
self.model.objects.exists()):
|
||||
raise LicenseForbids('Your Tower license only permits a single '
|
||||
'organization to exist.')
|
||||
raise LicenseForbids(_('Your Tower license only permits a single '
|
||||
'organization to exist.'))
|
||||
|
||||
# Okay, create the organization as usual.
|
||||
return super(OrganizationList, self).create(request, *args, **kwargs)
|
||||
@@ -766,8 +767,8 @@ class OrganizationActivityStreamList(SubListAPIView):
|
||||
# Sanity check: Does this license allow activity streams?
|
||||
# If not, forbid this request.
|
||||
if not feature_enabled('activity_streams'):
|
||||
raise LicenseForbids('Your license does not allow use of '
|
||||
'the activity stream.')
|
||||
raise LicenseForbids(_('Your license does not allow use of '
|
||||
'the activity stream.'))
|
||||
|
||||
# Okay, let it through.
|
||||
return super(OrganizationActivityStreamList, self).get(request, *args, **kwargs)
|
||||
@@ -860,20 +861,20 @@ class TeamRolesList(SubListCreateAttachDetachAPIView):
|
||||
# Forbid implicit role creation here
|
||||
sub_id = request.data.get('id', None)
|
||||
if not sub_id:
|
||||
data = dict(msg="Role 'id' field is missing.")
|
||||
data = dict(msg=_("Role 'id' field is missing."))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
role = get_object_or_400(Role, pk=sub_id)
|
||||
org_content_type = ContentType.objects.get_for_model(Organization)
|
||||
if role.content_type == org_content_type:
|
||||
data = dict(msg="You cannot assign an Organization role as a child role for a Team.")
|
||||
data = dict(msg=_("You cannot assign an Organization role as a child role for a Team."))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
team = get_object_or_404(Team, pk=self.kwargs['pk'])
|
||||
credential_content_type = ContentType.objects.get_for_model(Credential)
|
||||
if role.content_type == credential_content_type:
|
||||
if not role.content_object.organization or role.content_object.organization.id != team.organization.id:
|
||||
data = dict(msg="You cannot grant credential access to a team when the Organization field isn't set, or belongs to a different organization")
|
||||
data = dict(msg=_("You cannot grant credential access to a team when the Organization field isn't set, or belongs to a different organization"))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
return super(TeamRolesList, self).post(request, *args, **kwargs)
|
||||
@@ -919,8 +920,8 @@ class TeamActivityStreamList(SubListAPIView):
|
||||
# Sanity check: Does this license allow activity streams?
|
||||
# If not, forbid this request.
|
||||
if not feature_enabled('activity_streams'):
|
||||
raise LicenseForbids('Your license does not allow use of '
|
||||
'the activity stream.')
|
||||
raise LicenseForbids(_('Your license does not allow use of '
|
||||
'the activity stream.'))
|
||||
|
||||
# Okay, let it through.
|
||||
return super(TeamActivityStreamList, self).get(request, *args, **kwargs)
|
||||
@@ -966,7 +967,7 @@ class ProjectDetail(RetrieveUpdateDestroyAPIView):
|
||||
obj = self.get_object()
|
||||
can_delete = request.user.can_access(Project, 'delete', obj)
|
||||
if not can_delete:
|
||||
raise PermissionDenied("Cannot delete project.")
|
||||
raise PermissionDenied(_("Cannot delete project."))
|
||||
for pu in obj.project_updates.filter(status__in=['new', 'pending', 'waiting', 'running']):
|
||||
pu.cancel()
|
||||
return super(ProjectDetail, self).destroy(request, *args, **kwargs)
|
||||
@@ -992,7 +993,7 @@ class ProjectTeamsList(ListAPIView):
|
||||
|
||||
class ProjectSchedulesList(SubListCreateAttachDetachAPIView):
|
||||
|
||||
view_name = "Project Schedules"
|
||||
view_name = _("Project Schedules")
|
||||
|
||||
model = Schedule
|
||||
serializer_class = ScheduleSerializer
|
||||
@@ -1013,8 +1014,8 @@ class ProjectActivityStreamList(SubListAPIView):
|
||||
# Sanity check: Does this license allow activity streams?
|
||||
# If not, forbid this request.
|
||||
if not feature_enabled('activity_streams'):
|
||||
raise LicenseForbids('Your license does not allow use of '
|
||||
'the activity stream.')
|
||||
raise LicenseForbids(_('Your license does not allow use of '
|
||||
'the activity stream.'))
|
||||
|
||||
# Okay, let it through.
|
||||
return super(ProjectActivityStreamList, self).get(request, *args, **kwargs)
|
||||
@@ -1161,7 +1162,7 @@ class UserMeList(ListAPIView):
|
||||
|
||||
model = User
|
||||
serializer_class = UserSerializer
|
||||
view_name = 'Me'
|
||||
view_name = _('Me')
|
||||
|
||||
def get_queryset(self):
|
||||
return self.model.objects.filter(pk=self.request.user.pk)
|
||||
@@ -1199,26 +1200,26 @@ class UserRolesList(SubListCreateAttachDetachAPIView):
|
||||
# Forbid implicit role creation here
|
||||
sub_id = request.data.get('id', None)
|
||||
if not sub_id:
|
||||
data = dict(msg="Role 'id' field is missing.")
|
||||
data = dict(msg=_("Role 'id' field is missing."))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if sub_id == self.request.user.admin_role.pk:
|
||||
raise PermissionDenied('You may not perform any action with your own admin_role.')
|
||||
raise PermissionDenied(_('You may not perform any action with your own admin_role.'))
|
||||
|
||||
user = get_object_or_400(User, pk=self.kwargs['pk'])
|
||||
role = get_object_or_400(Role, pk=sub_id)
|
||||
user_content_type = ContentType.objects.get_for_model(User)
|
||||
if role.content_type == user_content_type:
|
||||
raise PermissionDenied('You may not change the membership of a users admin_role')
|
||||
raise PermissionDenied(_('You may not change the membership of a users admin_role'))
|
||||
|
||||
credential_content_type = ContentType.objects.get_for_model(Credential)
|
||||
if role.content_type == credential_content_type:
|
||||
if role.content_object.organization and user not in role.content_object.organization.member_role:
|
||||
data = dict(msg="You cannot grant credential access to a user not in the credentials' organization")
|
||||
data = dict(msg=_("You cannot grant credential access to a user not in the credentials' organization"))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if not role.content_object.organization and not request.user.is_superuser:
|
||||
data = dict(msg="You cannot grant private credential access to another user")
|
||||
data = dict(msg=_("You cannot grant private credential access to another user"))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
@@ -1281,8 +1282,8 @@ class UserActivityStreamList(SubListAPIView):
|
||||
# Sanity check: Does this license allow activity streams?
|
||||
# If not, forbid this request.
|
||||
if not feature_enabled('activity_streams'):
|
||||
raise LicenseForbids('Your license does not allow use of '
|
||||
'the activity stream.')
|
||||
raise LicenseForbids(_('Your license does not allow use of '
|
||||
'the activity stream.'))
|
||||
|
||||
# Okay, let it through.
|
||||
return super(UserActivityStreamList, self).get(request, *args, **kwargs)
|
||||
@@ -1322,13 +1323,13 @@ class UserDetail(RetrieveUpdateDestroyAPIView):
|
||||
if left is not None and right is not None and left != right:
|
||||
bad_changes[field] = (left, right)
|
||||
if bad_changes:
|
||||
raise PermissionDenied('Cannot change %s.' % ', '.join(bad_changes.keys()))
|
||||
raise PermissionDenied(_('Cannot change %s.') % ', '.join(bad_changes.keys()))
|
||||
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
obj = self.get_object()
|
||||
can_delete = request.user.can_access(User, 'delete', obj)
|
||||
if not can_delete:
|
||||
raise PermissionDenied('Cannot delete user.')
|
||||
raise PermissionDenied(_('Cannot delete user.'))
|
||||
return super(UserDetail, self).destroy(request, *args, **kwargs)
|
||||
|
||||
class UserAccessList(ResourceAccessList):
|
||||
@@ -1440,8 +1441,8 @@ class CredentialActivityStreamList(SubListAPIView):
|
||||
# Sanity check: Does this license allow activity streams?
|
||||
# If not, forbid this request.
|
||||
if not feature_enabled('activity_streams'):
|
||||
raise LicenseForbids('Your license does not allow use of '
|
||||
'the activity stream.')
|
||||
raise LicenseForbids(_('Your license does not allow use of '
|
||||
'the activity stream.'))
|
||||
|
||||
# Okay, let it through.
|
||||
return super(CredentialActivityStreamList, self).get(request, *args, **kwargs)
|
||||
@@ -1478,7 +1479,7 @@ class InventoryScriptDetail(RetrieveUpdateDestroyAPIView):
|
||||
instance = self.get_object()
|
||||
can_delete = request.user.can_access(self.model, 'delete', instance)
|
||||
if not can_delete:
|
||||
raise PermissionDenied("Cannot delete inventory script.")
|
||||
raise PermissionDenied(_("Cannot delete inventory script."))
|
||||
for inv_src in InventorySource.objects.filter(source_script=instance):
|
||||
inv_src.source_script = None
|
||||
inv_src.save()
|
||||
@@ -1529,8 +1530,8 @@ class InventoryActivityStreamList(SubListAPIView):
|
||||
# Sanity check: Does this license allow activity streams?
|
||||
# If not, forbid this request.
|
||||
if not feature_enabled('activity_streams'):
|
||||
raise LicenseForbids('Your license does not allow use of '
|
||||
'the activity stream.')
|
||||
raise LicenseForbids(_('Your license does not allow use of '
|
||||
'the activity stream.'))
|
||||
|
||||
# Okay, let it through.
|
||||
return super(InventoryActivityStreamList, self).get(request, *args, **kwargs)
|
||||
@@ -1662,8 +1663,8 @@ class HostActivityStreamList(SubListAPIView):
|
||||
# Sanity check: Does this license allow activity streams?
|
||||
# If not, forbid this request.
|
||||
if not feature_enabled('activity_streams'):
|
||||
raise LicenseForbids('Your license does not allow use of '
|
||||
'the activity stream.')
|
||||
raise LicenseForbids(_('Your license does not allow use of '
|
||||
'the activity stream.'))
|
||||
|
||||
# Okay, let it through.
|
||||
return super(HostActivityStreamList, self).get(request, *args, **kwargs)
|
||||
@@ -1680,8 +1681,8 @@ class SystemTrackingEnforcementMixin(APIView):
|
||||
'''
|
||||
def check_permissions(self, request):
|
||||
if not feature_enabled("system_tracking"):
|
||||
raise LicenseForbids("Your license does not permit use "
|
||||
"of system tracking.")
|
||||
raise LicenseForbids(_("Your license does not permit use "
|
||||
"of system tracking."))
|
||||
return super(SystemTrackingEnforcementMixin, self).check_permissions(request)
|
||||
|
||||
class HostFactVersionsList(ListAPIView, ParentMixin, SystemTrackingEnforcementMixin):
|
||||
@@ -1725,7 +1726,7 @@ class HostFactCompareView(SubDetailAPIView, SystemTrackingEnforcementMixin):
|
||||
|
||||
fact_entry = Fact.get_host_fact(host_obj.id, module_spec, datetime_actual)
|
||||
if not fact_entry:
|
||||
return Response({'detail': 'Fact not found.'}, status=status.HTTP_404_NOT_FOUND)
|
||||
return Response({'detail': _('Fact not found.')}, status=status.HTTP_404_NOT_FOUND)
|
||||
return Response(self.serializer_class(instance=fact_entry).data)
|
||||
|
||||
class GroupList(ListCreateAPIView):
|
||||
@@ -1847,8 +1848,8 @@ class GroupActivityStreamList(SubListAPIView):
|
||||
# Sanity check: Does this license allow activity streams?
|
||||
# If not, forbid this request.
|
||||
if not feature_enabled('activity_streams'):
|
||||
raise LicenseForbids('Your license does not allow use of '
|
||||
'the activity stream.')
|
||||
raise LicenseForbids(_('Your license does not allow use of '
|
||||
'the activity stream.'))
|
||||
|
||||
# Okay, let it through.
|
||||
return super(GroupActivityStreamList, self).get(request, *args, **kwargs)
|
||||
@@ -2037,7 +2038,7 @@ class InventoryInventorySourcesList(SubListAPIView):
|
||||
serializer_class = InventorySourceSerializer
|
||||
parent_model = Inventory
|
||||
relationship = None # Not defined since using get_queryset().
|
||||
view_name = 'Inventory Source List'
|
||||
view_name = _('Inventory Source List')
|
||||
new_in_14 = True
|
||||
|
||||
def get_queryset(self):
|
||||
@@ -2063,14 +2064,14 @@ class InventorySourceDetail(RetrieveUpdateAPIView):
|
||||
obj = self.get_object()
|
||||
can_delete = request.user.can_access(InventorySource, 'delete', obj)
|
||||
if not can_delete:
|
||||
raise PermissionDenied("Cannot delete inventory source.")
|
||||
raise PermissionDenied(_("Cannot delete inventory source."))
|
||||
for pu in obj.inventory_updates.filter(status__in=['new', 'pending', 'waiting', 'running']):
|
||||
pu.cancel()
|
||||
return super(InventorySourceDetail, self).destroy(request, *args, **kwargs)
|
||||
|
||||
class InventorySourceSchedulesList(SubListCreateAttachDetachAPIView):
|
||||
|
||||
view_name = "Inventory Source Schedules"
|
||||
view_name = _("Inventory Source Schedules")
|
||||
|
||||
model = Schedule
|
||||
serializer_class = ScheduleSerializer
|
||||
@@ -2091,8 +2092,8 @@ class InventorySourceActivityStreamList(SubListAPIView):
|
||||
# Sanity check: Does this license allow activity streams?
|
||||
# If not, forbid this request.
|
||||
if not feature_enabled('activity_streams'):
|
||||
raise LicenseForbids('Your license does not allow use of '
|
||||
'the activity stream.')
|
||||
raise LicenseForbids(_('Your license does not allow use of '
|
||||
'the activity stream.'))
|
||||
|
||||
# Okay, let it through.
|
||||
return super(InventorySourceActivityStreamList, self).get(request, *args, **kwargs)
|
||||
@@ -2107,7 +2108,7 @@ class InventorySourceNotificationTemplatesAnyList(SubListCreateAttachDetachAPIVi
|
||||
def post(self, request, *args, **kwargs):
|
||||
parent = self.get_parent_object()
|
||||
if parent.source not in CLOUD_INVENTORY_SOURCES:
|
||||
return Response(dict(msg="Notification Templates can only be assigned when source is one of {}."
|
||||
return Response(dict(msg=_("Notification Templates can only be assigned when source is one of {}.")
|
||||
.format(CLOUD_INVENTORY_SOURCES, parent.source)),
|
||||
status=status.HTTP_400_BAD_REQUEST)
|
||||
return super(InventorySourceNotificationTemplatesAnyList, self).post(request, *args, **kwargs)
|
||||
@@ -2297,7 +2298,7 @@ class JobTemplateLaunch(RetrieveAPIView, GenericAPIView):
|
||||
|
||||
class JobTemplateSchedulesList(SubListCreateAttachDetachAPIView):
|
||||
|
||||
view_name = "Job Template Schedules"
|
||||
view_name = _("Job Template Schedules")
|
||||
|
||||
model = Schedule
|
||||
serializer_class = ScheduleSerializer
|
||||
@@ -2315,8 +2316,8 @@ class JobTemplateSurveySpec(GenericAPIView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
obj = self.get_object()
|
||||
if not feature_enabled('surveys'):
|
||||
raise LicenseForbids('Your license does not allow '
|
||||
'adding surveys.')
|
||||
raise LicenseForbids(_('Your license does not allow '
|
||||
'adding surveys.'))
|
||||
return Response(obj.survey_spec)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
@@ -2325,42 +2326,43 @@ class JobTemplateSurveySpec(GenericAPIView):
|
||||
# Sanity check: Are surveys available on this license?
|
||||
# If not, do not allow them to be used.
|
||||
if not feature_enabled('surveys'):
|
||||
raise LicenseForbids('Your license does not allow '
|
||||
'adding surveys.')
|
||||
raise LicenseForbids(_('Your license does not allow '
|
||||
'adding surveys.'))
|
||||
|
||||
if not request.user.can_access(self.model, 'change', obj, None):
|
||||
raise PermissionDenied()
|
||||
try:
|
||||
obj.survey_spec = json.dumps(request.data)
|
||||
except ValueError:
|
||||
return Response(dict(error="Invalid JSON when parsing survey spec."), status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(dict(error=_("Invalid JSON when parsing survey spec.")), status=status.HTTP_400_BAD_REQUEST)
|
||||
if "name" not in obj.survey_spec:
|
||||
return Response(dict(error="'name' missing from survey spec."), status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(dict(error=_("'name' missing from survey spec.")), status=status.HTTP_400_BAD_REQUEST)
|
||||
if "description" not in obj.survey_spec:
|
||||
return Response(dict(error="'description' missing from survey spec."), status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(dict(error=_("'description' missing from survey spec.")), status=status.HTTP_400_BAD_REQUEST)
|
||||
if "spec" not in obj.survey_spec:
|
||||
return Response(dict(error="'spec' missing from survey spec."), status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(dict(error=_("'spec' missing from survey spec.")), status=status.HTTP_400_BAD_REQUEST)
|
||||
if not isinstance(obj.survey_spec["spec"], list):
|
||||
return Response(dict(error="'spec' must be a list of items."), status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(dict(error=_("'spec' must be a list of items.")), status=status.HTTP_400_BAD_REQUEST)
|
||||
if len(obj.survey_spec["spec"]) < 1:
|
||||
return Response(dict(error="'spec' doesn't contain any items."), status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(dict(error=_("'spec' doesn't contain any items.")), status=status.HTTP_400_BAD_REQUEST)
|
||||
idx = 0
|
||||
variable_set = set()
|
||||
for survey_item in obj.survey_spec["spec"]:
|
||||
if not isinstance(survey_item, dict):
|
||||
return Response(dict(error="Survey question %s is not a json object." % str(idx)), status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(dict(error=_("Survey question %s is not a json object.") % str(idx)), status=status.HTTP_400_BAD_REQUEST)
|
||||
if "type" not in survey_item:
|
||||
return Response(dict(error="'type' missing from survey question %s." % str(idx)), status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(dict(error=_("'type' missing from survey question %s.") % str(idx)), status=status.HTTP_400_BAD_REQUEST)
|
||||
if "question_name" not in survey_item:
|
||||
return Response(dict(error="'question_name' missing from survey question %s." % str(idx)), status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(dict(error=_("'question_name' missing from survey question %s.") % str(idx)), status=status.HTTP_400_BAD_REQUEST)
|
||||
if "variable" not in survey_item:
|
||||
return Response(dict(error="'variable' missing from survey question %s." % str(idx)), status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(dict(error=_("'variable' missing from survey question %s.") % str(idx)), status=status.HTTP_400_BAD_REQUEST)
|
||||
if survey_item['variable'] in variable_set:
|
||||
return Response(dict(error="'variable' '%s' duplicated in survey question %s." % (survey_item['variable'], str(idx))), status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(dict(error=_("'variable' '%(item)s' duplicated in survey question %(survey)s.") % {
|
||||
'item': survey_item['variable'], 'survey': str(idx)}), status=status.HTTP_400_BAD_REQUEST)
|
||||
else:
|
||||
variable_set.add(survey_item['variable'])
|
||||
if "required" not in survey_item:
|
||||
return Response(dict(error="'required' missing from survey question %s." % str(idx)), status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response(dict(error=_("'required' missing from survey question %s.") % str(idx)), status=status.HTTP_400_BAD_REQUEST)
|
||||
idx += 1
|
||||
obj.save()
|
||||
return Response()
|
||||
@@ -2385,8 +2387,8 @@ class JobTemplateActivityStreamList(SubListAPIView):
|
||||
# Sanity check: Does this license allow activity streams?
|
||||
# If not, forbid this request.
|
||||
if not feature_enabled('activity_streams'):
|
||||
raise LicenseForbids('Your license does not allow use of '
|
||||
'the activity stream.')
|
||||
raise LicenseForbids(_('Your license does not allow use of '
|
||||
'the activity stream.'))
|
||||
|
||||
# Okay, let it through.
|
||||
return super(JobTemplateActivityStreamList, self).get(request, *args, **kwargs)
|
||||
@@ -2554,22 +2556,22 @@ class JobTemplateCallback(GenericAPIView):
|
||||
matching_hosts = self.find_matching_hosts()
|
||||
# Check matching hosts.
|
||||
if not matching_hosts:
|
||||
data = dict(msg='No matching host could be found!')
|
||||
data = dict(msg=_('No matching host could be found!'))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
elif len(matching_hosts) > 1:
|
||||
data = dict(msg='Multiple hosts matched the request!')
|
||||
data = dict(msg=_('Multiple hosts matched the request!'))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
else:
|
||||
host = list(matching_hosts)[0]
|
||||
if not job_template.can_start_without_user_input():
|
||||
data = dict(msg='Cannot start automatically, user input required!')
|
||||
data = dict(msg=_('Cannot start automatically, user input required!'))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
limit = host.name
|
||||
|
||||
# NOTE: We limit this to one job waiting per host per callblack to keep them from stacking crazily
|
||||
if Job.objects.filter(status__in=['pending', 'waiting', 'running'], job_template=job_template,
|
||||
limit=limit).count() > 0:
|
||||
data = dict(msg='Host callback job already pending.')
|
||||
data = dict(msg=_('Host callback job already pending.'))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Everything is fine; actually create the job.
|
||||
@@ -2582,7 +2584,7 @@ class JobTemplateCallback(GenericAPIView):
|
||||
kv['extra_vars'] = extra_vars
|
||||
result = job.signal_start(**kv)
|
||||
if not result:
|
||||
data = dict(msg='Error starting job!')
|
||||
data = dict(msg=_('Error starting job!'))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Return the location of the new job.
|
||||
@@ -2856,7 +2858,7 @@ class SystemJobTemplateList(ListAPIView):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if not request.user.is_superuser and not request.user.is_system_auditor:
|
||||
raise PermissionDenied("Superuser privileges needed.")
|
||||
raise PermissionDenied(_("Superuser privileges needed."))
|
||||
return super(SystemJobTemplateList, self).get(request, *args, **kwargs)
|
||||
|
||||
class SystemJobTemplateDetail(RetrieveAPIView):
|
||||
@@ -2883,7 +2885,7 @@ class SystemJobTemplateLaunch(GenericAPIView):
|
||||
|
||||
class SystemJobTemplateSchedulesList(SubListCreateAttachDetachAPIView):
|
||||
|
||||
view_name = "System Job Template Schedules"
|
||||
view_name = _("System Job Template Schedules")
|
||||
|
||||
model = Schedule
|
||||
serializer_class = ScheduleSerializer
|
||||
@@ -2966,8 +2968,8 @@ class JobActivityStreamList(SubListAPIView):
|
||||
# Sanity check: Does this license allow activity streams?
|
||||
# If not, forbid this request.
|
||||
if not feature_enabled('activity_streams'):
|
||||
raise LicenseForbids('Your license does not allow use of '
|
||||
'the activity stream.')
|
||||
raise LicenseForbids(_('Your license does not allow use of '
|
||||
'the activity stream.'))
|
||||
|
||||
# Okay, let it through.
|
||||
return super(JobActivityStreamList, self).get(request, *args, **kwargs)
|
||||
@@ -3060,7 +3062,7 @@ class BaseJobHostSummariesList(SubListAPIView):
|
||||
serializer_class = JobHostSummarySerializer
|
||||
parent_model = None # Subclasses must define this attribute.
|
||||
relationship = 'job_host_summaries'
|
||||
view_name = 'Job Host Summaries List'
|
||||
view_name = _('Job Host Summaries List')
|
||||
|
||||
class HostJobHostSummariesList(BaseJobHostSummariesList):
|
||||
|
||||
@@ -3095,7 +3097,7 @@ class JobEventChildrenList(SubListAPIView):
|
||||
serializer_class = JobEventSerializer
|
||||
parent_model = JobEvent
|
||||
relationship = 'children'
|
||||
view_name = 'Job Event Children List'
|
||||
view_name = _('Job Event Children List')
|
||||
|
||||
class JobEventHostsList(SubListAPIView):
|
||||
|
||||
@@ -3103,7 +3105,7 @@ class JobEventHostsList(SubListAPIView):
|
||||
serializer_class = HostSerializer
|
||||
parent_model = JobEvent
|
||||
relationship = 'hosts'
|
||||
view_name = 'Job Event Hosts List'
|
||||
view_name = _('Job Event Hosts List')
|
||||
|
||||
class BaseJobEventsList(SubListAPIView):
|
||||
|
||||
@@ -3111,7 +3113,7 @@ class BaseJobEventsList(SubListAPIView):
|
||||
serializer_class = JobEventSerializer
|
||||
parent_model = None # Subclasses must define this attribute.
|
||||
relationship = 'job_events'
|
||||
view_name = 'Job Events List'
|
||||
view_name = _('Job Events List')
|
||||
|
||||
class HostJobEventsList(BaseJobEventsList):
|
||||
|
||||
@@ -3128,7 +3130,7 @@ class JobJobEventsList(BaseJobEventsList):
|
||||
class JobJobPlaysList(BaseJobEventsList):
|
||||
|
||||
parent_model = Job
|
||||
view_name = 'Job Plays List'
|
||||
view_name = _('Job Plays List')
|
||||
new_in_200 = True
|
||||
|
||||
@paginated
|
||||
@@ -3203,7 +3205,7 @@ class JobJobTasksList(BaseJobEventsList):
|
||||
and their completion status.
|
||||
"""
|
||||
parent_model = Job
|
||||
view_name = 'Job Play Tasks List'
|
||||
view_name = _('Job Play Tasks List')
|
||||
new_in_200 = True
|
||||
|
||||
@paginated
|
||||
@@ -3218,15 +3220,15 @@ class JobJobTasksList(BaseJobEventsList):
|
||||
# If there's no event ID specified, this will return a 404.
|
||||
job = Job.objects.filter(pk=self.kwargs['pk'])
|
||||
if not job.exists():
|
||||
return ({'detail': 'Job not found.'}, -1, status.HTTP_404_NOT_FOUND)
|
||||
return ({'detail': _('Job not found.')}, -1, status.HTTP_404_NOT_FOUND)
|
||||
job = job[0]
|
||||
|
||||
if 'event_id' not in request.query_params:
|
||||
return ({"detail": "'event_id' not provided."}, -1, status.HTTP_400_BAD_REQUEST)
|
||||
return ({"detail": _("'event_id' not provided.")}, -1, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
parent_task = job.job_events.filter(pk=int(request.query_params.get('event_id', -1)))
|
||||
if not parent_task.exists():
|
||||
return ({'detail': 'Parent event not found.'}, -1, status.HTTP_404_NOT_FOUND)
|
||||
return ({'detail': _('Parent event not found.')}, -1, status.HTTP_404_NOT_FOUND)
|
||||
parent_task = parent_task[0]
|
||||
|
||||
STARTING_EVENTS = ('playbook_on_task_start', 'playbook_on_setup')
|
||||
@@ -3498,7 +3500,7 @@ class BaseAdHocCommandEventsList(SubListAPIView):
|
||||
serializer_class = AdHocCommandEventSerializer
|
||||
parent_model = None # Subclasses must define this attribute.
|
||||
relationship = 'ad_hoc_command_events'
|
||||
view_name = 'Ad Hoc Command Events List'
|
||||
view_name = _('Ad Hoc Command Events List')
|
||||
new_in_220 = True
|
||||
|
||||
|
||||
@@ -3529,8 +3531,8 @@ class AdHocCommandActivityStreamList(SubListAPIView):
|
||||
# Sanity check: Does this license allow activity streams?
|
||||
# If not, forbid this request.
|
||||
if not feature_enabled('activity_streams'):
|
||||
raise LicenseForbids('Your license does not allow use of '
|
||||
'the activity stream.')
|
||||
raise LicenseForbids(_('Your license does not allow use of '
|
||||
'the activity stream.'))
|
||||
|
||||
# Okay, let it through.
|
||||
return super(AdHocCommandActivityStreamList, self).get(request, *args, **kwargs)
|
||||
@@ -3551,7 +3553,7 @@ class SystemJobList(ListCreateAPIView):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if not request.user.is_superuser and not request.user.is_system_auditor:
|
||||
raise PermissionDenied("Superuser privileges needed.")
|
||||
raise PermissionDenied(_("Superuser privileges needed."))
|
||||
return super(SystemJobList, self).get(request, *args, **kwargs)
|
||||
|
||||
|
||||
@@ -3607,8 +3609,9 @@ class UnifiedJobStdout(RetrieveAPIView):
|
||||
unified_job = self.get_object()
|
||||
obj_size = unified_job.result_stdout_size
|
||||
if request.accepted_renderer.format != 'txt_download' and obj_size > settings.STDOUT_MAX_BYTES_DISPLAY:
|
||||
response_message = "Standard Output too large to display (%d bytes), only download supported for sizes over %d bytes" % (obj_size,
|
||||
settings.STDOUT_MAX_BYTES_DISPLAY)
|
||||
response_message = _("Standard Output too large to display (%(text_size)d bytes), "
|
||||
"only download supported for sizes over %(supported_size)d bytes") % {
|
||||
'text_size': obj_size, 'supported_size': settings.STDOUT_MAX_BYTES_DISPLAY}
|
||||
if request.accepted_renderer.format == 'json':
|
||||
return Response({'range': {'start': 0, 'end': 1, 'absolute_end': 1}, 'content': response_message})
|
||||
else:
|
||||
@@ -3655,7 +3658,7 @@ class UnifiedJobStdout(RetrieveAPIView):
|
||||
response["Content-Disposition"] = 'attachment; filename="job_%s.txt"' % str(unified_job.id)
|
||||
return response
|
||||
except Exception as e:
|
||||
return Response({"error": "Error generating stdout download file: %s" % str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return Response({"error": _("Error generating stdout download file: %s") % str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
elif request.accepted_renderer.format == 'txt':
|
||||
return Response(unified_job.result_stdout)
|
||||
else:
|
||||
@@ -3695,13 +3698,13 @@ class NotificationTemplateDetail(RetrieveUpdateDestroyAPIView):
|
||||
if not request.user.can_access(self.model, 'delete', obj):
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
if obj.notifications.filter(status='pending').exists():
|
||||
return Response({"error": "Delete not allowed while there are pending notifications"},
|
||||
return Response({"error": _("Delete not allowed while there are pending notifications")},
|
||||
status=status.HTTP_405_METHOD_NOT_ALLOWED)
|
||||
return super(NotificationTemplateDetail, self).delete(request, *args, **kwargs)
|
||||
|
||||
class NotificationTemplateTest(GenericAPIView):
|
||||
|
||||
view_name = 'NotificationTemplate Test'
|
||||
view_name = _('NotificationTemplate Test')
|
||||
model = NotificationTemplate
|
||||
serializer_class = EmptySerializer
|
||||
new_in_300 = True
|
||||
@@ -3762,8 +3765,8 @@ class ActivityStreamList(SimpleListAPIView):
|
||||
# Sanity check: Does this license allow activity streams?
|
||||
# If not, forbid this request.
|
||||
if not feature_enabled('activity_streams'):
|
||||
raise LicenseForbids('Your license does not allow use of '
|
||||
'the activity stream.')
|
||||
raise LicenseForbids(_('Your license does not allow use of '
|
||||
'the activity stream.'))
|
||||
|
||||
# Okay, let it through.
|
||||
return super(ActivityStreamList, self).get(request, *args, **kwargs)
|
||||
@@ -3779,8 +3782,8 @@ class ActivityStreamDetail(RetrieveAPIView):
|
||||
# Sanity check: Does this license allow activity streams?
|
||||
# If not, forbid this request.
|
||||
if not feature_enabled('activity_streams'):
|
||||
raise LicenseForbids('Your license does not allow use of '
|
||||
'the activity stream.')
|
||||
raise LicenseForbids(_('Your license does not allow use of '
|
||||
'the activity stream.'))
|
||||
|
||||
# Okay, let it through.
|
||||
return super(ActivityStreamDetail, self).get(request, *args, **kwargs)
|
||||
@@ -3830,26 +3833,26 @@ class RoleUsersList(SubListCreateAttachDetachAPIView):
|
||||
# Forbid implicit user creation here
|
||||
sub_id = request.data.get('id', None)
|
||||
if not sub_id:
|
||||
data = dict(msg="User 'id' field is missing.")
|
||||
data = dict(msg=_("User 'id' field is missing."))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
user = get_object_or_400(User, pk=sub_id)
|
||||
role = self.get_parent_object()
|
||||
if role == self.request.user.admin_role:
|
||||
raise PermissionDenied('You may not perform any action with your own admin_role.')
|
||||
raise PermissionDenied(_('You may not perform any action with your own admin_role.'))
|
||||
|
||||
user_content_type = ContentType.objects.get_for_model(User)
|
||||
if role.content_type == user_content_type:
|
||||
raise PermissionDenied('You may not change the membership of a users admin_role')
|
||||
raise PermissionDenied(_('You may not change the membership of a users admin_role'))
|
||||
|
||||
credential_content_type = ContentType.objects.get_for_model(Credential)
|
||||
if role.content_type == credential_content_type:
|
||||
if role.content_object.organization and user not in role.content_object.organization.member_role:
|
||||
data = dict(msg="You cannot grant credential access to a user not in the credentials' organization")
|
||||
data = dict(msg=_("You cannot grant credential access to a user not in the credentials' organization"))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if not role.content_object.organization and not request.user.is_superuser:
|
||||
data = dict(msg="You cannot grant private credential access to another user")
|
||||
data = dict(msg=_("You cannot grant private credential access to another user"))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
return super(RoleUsersList, self).post(request, *args, **kwargs)
|
||||
@@ -3873,7 +3876,7 @@ class RoleTeamsList(SubListAPIView):
|
||||
# Forbid implicit team creation here
|
||||
sub_id = request.data.get('id', None)
|
||||
if not sub_id:
|
||||
data = dict(msg="Team 'id' field is missing.")
|
||||
data = dict(msg=_("Team 'id' field is missing."))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
team = get_object_or_400(Team, pk=sub_id)
|
||||
@@ -3881,13 +3884,13 @@ class RoleTeamsList(SubListAPIView):
|
||||
|
||||
organization_content_type = ContentType.objects.get_for_model(Organization)
|
||||
if role.content_type == organization_content_type:
|
||||
data = dict(msg="You cannot assign an Organization role as a child role for a Team.")
|
||||
data = dict(msg=_("You cannot assign an Organization role as a child role for a Team."))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
credential_content_type = ContentType.objects.get_for_model(Credential)
|
||||
if role.content_type == credential_content_type:
|
||||
if not role.content_object.organization or role.content_object.organization.id != team.organization.id:
|
||||
data = dict(msg="You cannot grant credential access to a team when the Organization field isn't set, or belongs to a different organization")
|
||||
data = dict(msg=_("You cannot grant credential access to a team when the Organization field isn't set, or belongs to a different organization"))
|
||||
return Response(data, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
action = 'attach'
|
||||
|
||||
Reference in New Issue
Block a user