Merge branch 'ansible:devel' into hashicorp-vault-kubernetes-auth

This commit is contained in:
liortamari
2022-03-22 14:07:15 +02:00
committed by GitHub
365 changed files with 5077 additions and 3294 deletions

View File

@@ -5,7 +5,7 @@ env:
on: on:
pull_request: pull_request:
jobs: jobs:
common_tests: common-tests:
name: ${{ matrix.tests.name }} name: ${{ matrix.tests.name }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
@@ -33,9 +33,12 @@ jobs:
- name: ui-lint - name: ui-lint
label: Run UI Linters label: Run UI Linters
command: make ui-lint command: make ui-lint
- name: ui-test - name: ui-test-screens
label: Run UI Tests label: Run UI Screens Tests
command: make ui-test command: make ui-test-screens
- name: ui-test-general
label: Run UI General Tests
command: make ui-test-general
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@@ -63,6 +66,36 @@ jobs:
run: | run: |
docker run -u $(id -u) --rm -v ${{ github.workspace}}:/awx_devel/:Z \ docker run -u $(id -u) --rm -v ${{ github.workspace}}:/awx_devel/:Z \
--workdir=/awx_devel ghcr.io/${{ github.repository_owner }}/awx_devel:${{ env.BRANCH }} ${{ matrix.tests.command }} --workdir=/awx_devel ghcr.io/${{ github.repository_owner }}/awx_devel:${{ env.BRANCH }} ${{ matrix.tests.command }}
dev-env:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Get python version from Makefile
run: echo py_version=`make PYTHON_VERSION` >> $GITHUB_ENV
- name: Install python ${{ env.py_version }}
uses: actions/setup-python@v2
with:
python-version: ${{ env.py_version }}
- name: Log in to registry
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Pre-pull image to warm build cache
run: |
docker pull ghcr.io/${{ github.repository_owner }}/awx_devel:${{ env.BRANCH }} || :
- name: Build image
run: |
DEV_DOCKER_TAG_BASE=ghcr.io/${{ github.repository_owner }} COMPOSE_TAG=${{ env.BRANCH }} make docker-compose-build
- name: Run smoke test
run: |
export DEV_DOCKER_TAG_BASE=ghcr.io/${{ github.repository_owner }}
export COMPOSE_TAG=${{ env.BRANCH }}
ansible-playbook tools/docker-compose/ansible/smoke-test.yml -e repo_dir=$(pwd) -v
awx-operator: awx-operator:
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@@ -8,6 +8,53 @@ jobs:
promote: promote:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout awx
uses: actions/checkout@v2
- name: Get python version from Makefile
run: echo py_version=`make PYTHON_VERSION` >> $GITHUB_ENV
- name: Install python ${{ env.py_version }}
uses: actions/setup-python@v2
with:
python-version: ${{ env.py_version }}
- name: Install dependencies
run: |
python${{ env.py_version }} -m pip install wheel twine
- name: Set official collection namespace
run: echo collection_namespace=awx >> $GITHUB_ENV
if: ${{ github.repository_owner == 'ansible' }}
- name: Set unofficial collection namespace
run: echo collection_namespace=${{ github.repository_owner }} >> $GITHUB_ENV
if: ${{ github.repository_owner != 'ansible' }}
- name: Build collection and publish to galaxy
run: |
COLLECTION_NAMESPACE=${{ env.collection_namespace }} make build_collection
ansible-galaxy collection publish \
--token=${{ secrets.GALAXY_TOKEN }} \
awx_collection_build/${{ env.collection_namespace }}-awx-${{ github.event.release.tag_name }}.tar.gz
- name: Set official pypi info
run: echo pypi_repo=pypi >> $GITHUB_ENV
if: ${{ github.repository_owner == 'ansible' }}
- name: Set unofficial pypi info
run: echo pypi_repo=testpypi >> $GITHUB_ENV
if: ${{ github.repository_owner != 'ansible' }}
- name: Build awxkit and upload to pypi
run: |
cd awxkit && python3 setup.py bdist_wheel
twine upload \
-r ${{ env.pypi_repo }} \
-u ${{ secrets.PYPI_USERNAME }} \
-p ${{ secrets.PYPI_PASSWORD }} \
dist/*
- name: Log in to GHCR - name: Log in to GHCR
run: | run: |
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin

View File

@@ -83,7 +83,8 @@ jobs:
- name: Build and stage awx-operator - name: Build and stage awx-operator
working-directory: awx-operator working-directory: awx-operator
run: | run: |
BUILD_ARGS="--build-arg DEFAULT_AWX_VERSION=${{ github.event.inputs.version }}" \ BUILD_ARGS="--build-arg DEFAULT_AWX_VERSION=${{ github.event.inputs.version }} \
--build-arg OPERATOR_VERSION=${{ github.event.inputs.operator_version }}" \
IMAGE_TAG_BASE=ghcr.io/${{ github.repository_owner }}/awx-operator \ IMAGE_TAG_BASE=ghcr.io/${{ github.repository_owner }}/awx-operator \
VERSION=${{ github.event.inputs.operator_version }} make docker-build docker-push VERSION=${{ github.event.inputs.operator_version }} make docker-build docker-push

View File

@@ -13,6 +13,8 @@ COMPOSE_TAG ?= $(GIT_BRANCH)
MAIN_NODE_TYPE ?= hybrid MAIN_NODE_TYPE ?= hybrid
# If set to true docker-compose will also start a keycloak instance # If set to true docker-compose will also start a keycloak instance
KEYCLOAK ?= false KEYCLOAK ?= false
# If set to true docker-compose will also start an ldap instance
LDAP ?= false
VENV_BASE ?= /var/lib/awx/venv VENV_BASE ?= /var/lib/awx/venv
@@ -305,7 +307,7 @@ symlink_collection:
mkdir -p ~/.ansible/collections/ansible_collections/$(COLLECTION_NAMESPACE) # in case it does not exist mkdir -p ~/.ansible/collections/ansible_collections/$(COLLECTION_NAMESPACE) # in case it does not exist
ln -s $(shell pwd)/awx_collection $(COLLECTION_INSTALL) ln -s $(shell pwd)/awx_collection $(COLLECTION_INSTALL)
build_collection: awx_collection_build: $(shell find awx_collection -type f)
ansible-playbook -i localhost, awx_collection/tools/template_galaxy.yml \ ansible-playbook -i localhost, awx_collection/tools/template_galaxy.yml \
-e collection_package=$(COLLECTION_PACKAGE) \ -e collection_package=$(COLLECTION_PACKAGE) \
-e collection_namespace=$(COLLECTION_NAMESPACE) \ -e collection_namespace=$(COLLECTION_NAMESPACE) \
@@ -313,6 +315,8 @@ build_collection:
-e '{"awx_template_version":false}' -e '{"awx_template_version":false}'
ansible-galaxy collection build awx_collection_build --force --output-path=awx_collection_build ansible-galaxy collection build awx_collection_build --force --output-path=awx_collection_build
build_collection: awx_collection_build
install_collection: build_collection install_collection: build_collection
rm -rf $(COLLECTION_INSTALL) rm -rf $(COLLECTION_INSTALL)
ansible-galaxy collection install awx_collection_build/$(COLLECTION_NAMESPACE)-$(COLLECTION_PACKAGE)-$(COLLECTION_VERSION).tar.gz ansible-galaxy collection install awx_collection_build/$(COLLECTION_NAMESPACE)-$(COLLECTION_PACKAGE)-$(COLLECTION_VERSION).tar.gz
@@ -400,9 +404,18 @@ ui-lint:
ui-test: ui-test:
$(NPM_BIN) --prefix awx/ui install $(NPM_BIN) --prefix awx/ui install
$(NPM_BIN) run --prefix awx/ui test $(NPM_BIN) run --prefix awx/ui test
ui-test-screens:
$(NPM_BIN) --prefix awx/ui install
$(NPM_BIN) run --prefix awx/ui pretest
$(NPM_BIN) run --prefix awx/ui test-screens --runInBand
ui-test-general:
$(NPM_BIN) --prefix awx/ui install
$(NPM_BIN) run --prefix awx/ui pretest
$(NPM_BIN) run --prefix awx/ui/ test-general --runInBand
# Build a pip-installable package into dist/ with a timestamped version number. # Build a pip-installable package into dist/ with a timestamped version number.
dev_build: dev_build:
$(PYTHON) setup.py dev_build $(PYTHON) setup.py dev_build
@@ -451,7 +464,8 @@ docker-compose-sources: .git/hooks/pre-commit
-e control_plane_node_count=$(CONTROL_PLANE_NODE_COUNT) \ -e control_plane_node_count=$(CONTROL_PLANE_NODE_COUNT) \
-e execution_node_count=$(EXECUTION_NODE_COUNT) \ -e execution_node_count=$(EXECUTION_NODE_COUNT) \
-e minikube_container_group=$(MINIKUBE_CONTAINER_GROUP) \ -e minikube_container_group=$(MINIKUBE_CONTAINER_GROUP) \
-e enable_keycloak=$(KEYCLOAK) -e enable_keycloak=$(KEYCLOAK) \
-e enable_ldap=$(LDAP)
docker-compose: awx/projects docker-compose-sources docker-compose: awx/projects docker-compose-sources
@@ -567,3 +581,6 @@ messages:
. $(VENV_BASE)/awx/bin/activate; \ . $(VENV_BASE)/awx/bin/activate; \
fi; \ fi; \
$(PYTHON) manage.py makemessages -l $(LANG) --keep-pot $(PYTHON) manage.py makemessages -l $(LANG) --keep-pot
print-%:
@echo $($*)

3
SECURITY.md Normal file
View File

@@ -0,0 +1,3 @@
For all security related bugs, email security@ansible.com instead of using this issue tracker and you will receive a prompt response.
For more information on the Ansible community's practices regarding responsible disclosure, see https://www.ansible.com/security

View File

@@ -36,7 +36,6 @@ else:
from django.db.backends.utils import names_digest from django.db.backends.utils import names_digest
from django.db import connection from django.db import connection
if HAS_DJANGO is True: if HAS_DJANGO is True:
# See upgrade blocker note in requirements/README.md # See upgrade blocker note in requirements/README.md

View File

@@ -6,7 +6,7 @@ import logging
# Django # Django
from django.conf import settings from django.conf import settings
from django.utils.encoding import smart_text from django.utils.encoding import smart_str
# Django REST Framework # Django REST Framework
from rest_framework import authentication from rest_framework import authentication
@@ -24,7 +24,7 @@ class LoggedBasicAuthentication(authentication.BasicAuthentication):
ret = super(LoggedBasicAuthentication, self).authenticate(request) ret = super(LoggedBasicAuthentication, self).authenticate(request)
if ret: if ret:
username = ret[0].username if ret[0] else '<none>' username = ret[0].username if ret[0] else '<none>'
logger.info(smart_text(u"User {} performed a {} to {} through the API".format(username, request.method, request.path))) logger.info(smart_str(u"User {} performed a {} to {} through the API".format(username, request.method, request.path)))
return ret return ret
def authenticate_header(self, request): def authenticate_header(self, request):
@@ -45,7 +45,7 @@ class LoggedOAuth2Authentication(OAuth2Authentication):
user, token = ret user, token = ret
username = user.username if user else '<none>' username = user.username if user else '<none>'
logger.info( logger.info(
smart_text(u"User {} performed a {} to {} through the API using OAuth 2 token {}.".format(username, request.method, request.path, token.pk)) smart_str(u"User {} performed a {} to {} through the API using OAuth 2 token {}.".format(username, request.method, request.path, token.pk))
) )
setattr(user, 'oauth_scopes', [x for x in token.scope.split() if x]) setattr(user, 'oauth_scopes', [x for x in token.scope.split() if x])
return ret return ret

View File

@@ -1,6 +1,6 @@
# Django # Django
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
# Django REST Framework # Django REST Framework
from rest_framework import serializers from rest_framework import serializers

View File

@@ -2,7 +2,7 @@
# All Rights Reserved. # All Rights Reserved.
# Django # Django
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
# Django REST Framework # Django REST Framework
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
@@ -13,7 +13,7 @@ class ActiveJobConflict(ValidationError):
def __init__(self, active_jobs): def __init__(self, active_jobs):
# During APIException.__init__(), Django Rest Framework # During APIException.__init__(), Django Rest Framework
# turn everything in self.detail into string by using force_text. # turn everything in self.detail into string by using force_str.
# Declare detail afterwards circumvent this behavior. # Declare detail afterwards circumvent this behavior.
super(ActiveJobConflict, self).__init__() super(ActiveJobConflict, self).__init__()
self.detail = {"error": _("Resource is being used by running jobs."), "active_jobs": active_jobs} self.detail = {"error": _("Resource is being used by running jobs."), "active_jobs": active_jobs}

View File

@@ -2,7 +2,7 @@
# All Rights Reserved. # All Rights Reserved.
# Django # Django
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
# Django REST Framework # Django REST Framework
@@ -28,13 +28,17 @@ class NullFieldMixin(object):
return (is_empty_value, data) return (is_empty_value, data)
class BooleanNullField(NullFieldMixin, serializers.NullBooleanField): class BooleanNullField(NullFieldMixin, serializers.BooleanField):
""" """
Custom boolean field that allows null and empty string as False values. Custom boolean field that allows null and empty string as False values.
""" """
def __init__(self, **kwargs):
kwargs['allow_null'] = True
super().__init__(**kwargs)
def to_internal_value(self, data): def to_internal_value(self, data):
return bool(super(BooleanNullField, self).to_internal_value(data)) return bool(super().to_internal_value(data))
class CharNullField(NullFieldMixin, serializers.CharField): class CharNullField(NullFieldMixin, serializers.CharField):
@@ -47,7 +51,7 @@ class CharNullField(NullFieldMixin, serializers.CharField):
super(CharNullField, self).__init__(**kwargs) super(CharNullField, self).__init__(**kwargs)
def to_internal_value(self, data): def to_internal_value(self, data):
return super(CharNullField, self).to_internal_value(data or u'') return super(CharNullField, self).to_internal_value(data or '')
class ChoiceNullField(NullFieldMixin, serializers.ChoiceField): class ChoiceNullField(NullFieldMixin, serializers.ChoiceField):
@@ -60,7 +64,7 @@ class ChoiceNullField(NullFieldMixin, serializers.ChoiceField):
super(ChoiceNullField, self).__init__(**kwargs) super(ChoiceNullField, self).__init__(**kwargs)
def to_internal_value(self, data): def to_internal_value(self, data):
return super(ChoiceNullField, self).to_internal_value(data or u'') return super(ChoiceNullField, self).to_internal_value(data or '')
class VerbatimField(serializers.Field): class VerbatimField(serializers.Field):

View File

@@ -7,15 +7,15 @@ import json
from functools import reduce from functools import reduce
# Django # Django
from django.core.exceptions import FieldError, ValidationError from django.core.exceptions import FieldError, ValidationError, FieldDoesNotExist
from django.db import models from django.db import models
from django.db.models import Q, CharField, IntegerField, BooleanField from django.db.models import Q, CharField, IntegerField, BooleanField, TextField, JSONField
from django.db.models.fields import FieldDoesNotExist
from django.db.models.fields.related import ForeignObjectRel, ManyToManyField, ForeignKey from django.db.models.fields.related import ForeignObjectRel, ManyToManyField, ForeignKey
from django.db.models.functions import Cast
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericForeignKey
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
# Django REST Framework # Django REST Framework
from rest_framework.exceptions import ParseError, PermissionDenied from rest_framework.exceptions import ParseError, PermissionDenied
@@ -185,16 +185,14 @@ class FieldLookupBackend(BaseFilterBackend):
return (field_list[-1], new_lookup) return (field_list[-1], new_lookup)
def to_python_related(self, value): def to_python_related(self, value):
value = force_text(value) value = force_str(value)
if value.lower() in ('none', 'null'): if value.lower() in ('none', 'null'):
return None return None
else: else:
return int(value) return int(value)
def value_to_python_for_field(self, field, value): def value_to_python_for_field(self, field, value):
if isinstance(field, models.NullBooleanField): if isinstance(field, models.BooleanField):
return to_python_boolean(value, allow_none=True)
elif isinstance(field, models.BooleanField):
return to_python_boolean(value) return to_python_boolean(value)
elif isinstance(field, (ForeignObjectRel, ManyToManyField, GenericForeignKey, ForeignKey)): elif isinstance(field, (ForeignObjectRel, ManyToManyField, GenericForeignKey, ForeignKey)):
try: try:
@@ -244,6 +242,8 @@ class FieldLookupBackend(BaseFilterBackend):
new_lookups.append('{}__{}__icontains'.format(new_lookup[:-8], rm_field.name)) new_lookups.append('{}__{}__icontains'.format(new_lookup[:-8], rm_field.name))
return value, new_lookups, needs_distinct return value, new_lookups, needs_distinct
else: else:
if isinstance(field, JSONField):
new_lookup = new_lookup.replace(field.name, f'{field.name}_as_txt')
value = self.value_to_python_for_field(field, value) value = self.value_to_python_for_field(field, value)
return value, new_lookup, needs_distinct return value, new_lookup, needs_distinct
@@ -293,7 +293,7 @@ class FieldLookupBackend(BaseFilterBackend):
search_filter_relation = 'AND' search_filter_relation = 'AND'
values = reduce(lambda list1, list2: list1 + list2, [i.split(',') for i in values]) values = reduce(lambda list1, list2: list1 + list2, [i.split(',') for i in values])
for value in values: for value in values:
search_value, new_keys, _ = self.value_to_python(queryset.model, key, force_text(value)) search_value, new_keys, _ = self.value_to_python(queryset.model, key, force_str(value))
assert isinstance(new_keys, list) assert isinstance(new_keys, list)
search_filters[search_value] = new_keys search_filters[search_value] = new_keys
# by definition, search *only* joins across relations, # by definition, search *only* joins across relations,
@@ -325,6 +325,9 @@ class FieldLookupBackend(BaseFilterBackend):
value, new_key, distinct = self.value_to_python(queryset.model, key, value) value, new_key, distinct = self.value_to_python(queryset.model, key, value)
if distinct: if distinct:
needs_distinct = True needs_distinct = True
if '_as_txt' in new_key:
fname = next(item for item in new_key.split('__') if item.endswith('_as_txt'))
queryset = queryset.annotate(**{fname: Cast(fname[:-7], output_field=TextField())})
if q_chain: if q_chain:
chain_filters.append((q_not, new_key, value)) chain_filters.append((q_not, new_key, value))
elif q_or: elif q_or:

View File

@@ -10,18 +10,18 @@ import urllib.parse
# Django # Django
from django.conf import settings from django.conf import settings
from django.contrib.auth import views as auth_views
from django.contrib.contenttypes.models import ContentType
from django.core.cache import cache from django.core.cache import cache
from django.core.exceptions import FieldDoesNotExist
from django.db import connection from django.db import connection
from django.db.models.fields import FieldDoesNotExist
from django.db.models.fields.related import OneToOneRel from django.db.models.fields.related import OneToOneRel
from django.http import QueryDict from django.http import QueryDict
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.encoding import smart_text from django.utils.encoding import smart_str
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.contrib.contenttypes.models import ContentType from django.utils.translation import gettext_lazy as _
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth import views as auth_views
# Django REST Framework # Django REST Framework
from rest_framework.exceptions import PermissionDenied, AuthenticationFailed, ParseError, NotAcceptable, UnsupportedMediaType from rest_framework.exceptions import PermissionDenied, AuthenticationFailed, ParseError, NotAcceptable, UnsupportedMediaType
@@ -93,17 +93,18 @@ class LoggedLoginView(auth_views.LoginView):
ret = super(LoggedLoginView, self).post(request, *args, **kwargs) ret = super(LoggedLoginView, self).post(request, *args, **kwargs)
current_user = getattr(request, 'user', None) current_user = getattr(request, 'user', None)
if request.user.is_authenticated: if request.user.is_authenticated:
logger.info(smart_text(u"User {} logged in from {}".format(self.request.user.username, request.META.get('REMOTE_ADDR', None)))) logger.info(smart_str(u"User {} logged in from {}".format(self.request.user.username, request.META.get('REMOTE_ADDR', None))))
ret.set_cookie('userLoggedIn', 'true') ret.set_cookie('userLoggedIn', 'true')
current_user = UserSerializer(self.request.user) current_user = UserSerializer(self.request.user)
current_user = smart_text(JSONRenderer().render(current_user.data)) current_user = smart_str(JSONRenderer().render(current_user.data))
current_user = urllib.parse.quote('%s' % current_user, '') current_user = urllib.parse.quote('%s' % current_user, '')
ret.set_cookie('current_user', current_user, secure=settings.SESSION_COOKIE_SECURE or None) ret.set_cookie('current_user', current_user, secure=settings.SESSION_COOKIE_SECURE or None)
ret.setdefault('X-API-Session-Cookie-Name', getattr(settings, 'SESSION_COOKIE_NAME', 'awx_sessionid'))
return ret return ret
else: else:
if 'username' in self.request.POST: if 'username' in self.request.POST:
logger.warn(smart_text(u"Login failed for user {} from {}".format(self.request.POST.get('username'), request.META.get('REMOTE_ADDR', None)))) logger.warning(smart_str(u"Login failed for user {} from {}".format(self.request.POST.get('username'), request.META.get('REMOTE_ADDR', None))))
ret.status_code = 401 ret.status_code = 401
return ret return ret
@@ -391,8 +392,8 @@ class GenericAPIView(generics.GenericAPIView, APIView):
if hasattr(self.model._meta, "verbose_name"): if hasattr(self.model._meta, "verbose_name"):
d.update( d.update(
{ {
'model_verbose_name': smart_text(self.model._meta.verbose_name), 'model_verbose_name': smart_str(self.model._meta.verbose_name),
'model_verbose_name_plural': smart_text(self.model._meta.verbose_name_plural), 'model_verbose_name_plural': smart_str(self.model._meta.verbose_name_plural),
} }
) )
serializer = self.get_serializer() serializer = self.get_serializer()
@@ -523,8 +524,8 @@ class SubListAPIView(ParentMixin, ListAPIView):
d = super(SubListAPIView, self).get_description_context() d = super(SubListAPIView, self).get_description_context()
d.update( d.update(
{ {
'parent_model_verbose_name': smart_text(self.parent_model._meta.verbose_name), 'parent_model_verbose_name': smart_str(self.parent_model._meta.verbose_name),
'parent_model_verbose_name_plural': smart_text(self.parent_model._meta.verbose_name_plural), 'parent_model_verbose_name_plural': smart_str(self.parent_model._meta.verbose_name_plural),
} }
) )
return d return d

View File

@@ -6,11 +6,12 @@ from uuid import UUID
# Django # Django
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.db.models import JSONField
from django.db.models.fields import PositiveIntegerField, BooleanField from django.db.models.fields import PositiveIntegerField, BooleanField
from django.db.models.fields.related import ForeignKey from django.db.models.fields.related import ForeignKey
from django.http import Http404 from django.http import Http404
from django.utils.encoding import force_text, smart_text from django.utils.encoding import force_str, smart_str
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
# Django REST Framework # Django REST Framework
from rest_framework import exceptions from rest_framework import exceptions
@@ -22,7 +23,7 @@ from rest_framework.request import clone_request
# AWX # AWX
from awx.api.fields import ChoiceNullField from awx.api.fields import ChoiceNullField
from awx.main.fields import JSONField, ImplicitRoleField from awx.main.fields import ImplicitRoleField
from awx.main.models import NotificationTemplate from awx.main.models import NotificationTemplate
from awx.main.utils.execution_environments import get_default_pod_spec from awx.main.utils.execution_environments import get_default_pod_spec
@@ -53,7 +54,7 @@ class Metadata(metadata.SimpleMetadata):
for attr in text_attrs: for attr in text_attrs:
value = getattr(field, attr, None) value = getattr(field, attr, None)
if value is not None and value != '': if value is not None and value != '':
field_info[attr] = force_text(value, strings_only=True) field_info[attr] = force_str(value, strings_only=True)
placeholder = getattr(field, 'placeholder', serializers.empty) placeholder = getattr(field, 'placeholder', serializers.empty)
if placeholder is not serializers.empty: if placeholder is not serializers.empty:
@@ -77,7 +78,7 @@ class Metadata(metadata.SimpleMetadata):
} }
if field.field_name in field_help_text: if field.field_name in field_help_text:
opts = serializer.Meta.model._meta.concrete_model._meta opts = serializer.Meta.model._meta.concrete_model._meta
verbose_name = smart_text(opts.verbose_name) verbose_name = smart_str(opts.verbose_name)
field_info['help_text'] = field_help_text[field.field_name].format(verbose_name) field_info['help_text'] = field_help_text[field.field_name].format(verbose_name)
if field.field_name == 'type': if field.field_name == 'type':

View File

@@ -1,11 +1,11 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import MetricsView from awx.api.views import MetricsView
urls = [url(r'^$', MetricsView.as_view(), name='metrics_view')] urls = [re_path(r'^$', MetricsView.as_view(), name='metrics_view')]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -5,7 +5,7 @@ import json
# Django # Django
from django.conf import settings from django.conf import settings
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
# Django REST Framework # Django REST Framework
from rest_framework import parsers from rest_framework import parsers

View File

@@ -4,8 +4,6 @@
# Python # Python
import logging import logging
from django.conf import settings
# Django REST Framework # Django REST Framework
from rest_framework.exceptions import MethodNotAllowed, PermissionDenied from rest_framework.exceptions import MethodNotAllowed, PermissionDenied
from rest_framework import permissions from rest_framework import permissions
@@ -250,13 +248,6 @@ class IsSystemAdminOrAuditor(permissions.BasePermission):
return request.user.is_superuser return request.user.is_superuser
class InstanceGroupTowerPermission(ModelAccessPermission):
def has_object_permission(self, request, view, obj):
if request.method == 'DELETE' and obj.name in [settings.DEFAULT_EXECUTION_QUEUE_NAME, settings.DEFAULT_CONTROL_PLANE_QUEUE_NAME]:
return False
return super(InstanceGroupTowerPermission, self).has_object_permission(request, view, obj)
class WebhookKeyPermission(permissions.BasePermission): class WebhookKeyPermission(permissions.BasePermission):
def has_object_permission(self, request, view, obj): def has_object_permission(self, request, view, obj):
return request.user.can_access(view.model, 'admin', obj, request.data) return request.user.can_access(view.model, 'admin', obj, request.data)

View File

@@ -25,8 +25,8 @@ from django.contrib.auth.password_validation import validate_password as django_
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist, ValidationError as DjangoValidationError from django.core.exceptions import ObjectDoesNotExist, ValidationError as DjangoValidationError
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.utils.encoding import force_text from django.utils.encoding import force_str
from django.utils.text import capfirst from django.utils.text import capfirst
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.functional import cached_property from django.utils.functional import cached_property
@@ -97,7 +97,7 @@ from awx.main.models import (
) )
from awx.main.models.base import VERBOSITY_CHOICES, NEW_JOB_TYPE_CHOICES from awx.main.models.base import VERBOSITY_CHOICES, NEW_JOB_TYPE_CHOICES
from awx.main.models.rbac import get_roles_on_resource, role_summary_fields_generator from awx.main.models.rbac import get_roles_on_resource, role_summary_fields_generator
from awx.main.fields import ImplicitRoleField, JSONBField from awx.main.fields import ImplicitRoleField
from awx.main.utils import ( from awx.main.utils import (
get_type_for_model, get_type_for_model,
get_model_for_type, get_model_for_type,
@@ -357,7 +357,7 @@ class BaseSerializer(serializers.ModelSerializer, metaclass=BaseSerializerMetacl
} }
choices = [] choices = []
for t in self.get_types(): for t in self.get_types():
name = _(type_name_map.get(t, force_text(get_model_for_type(t)._meta.verbose_name).title())) name = _(type_name_map.get(t, force_str(get_model_for_type(t)._meta.verbose_name).title()))
choices.append((t, name)) choices.append((t, name))
return choices return choices
@@ -645,7 +645,7 @@ class BaseSerializer(serializers.ModelSerializer, metaclass=BaseSerializerMetacl
v2.extend(e) v2.extend(e)
else: else:
v2.append(e) v2.append(e)
d[k] = list(map(force_text, v2)) d[k] = list(map(force_str, v2))
raise ValidationError(d) raise ValidationError(d)
return attrs return attrs
@@ -1263,6 +1263,12 @@ class OAuth2ApplicationSerializer(BaseSerializer):
activity_stream=self.reverse('api:o_auth2_application_activity_stream_list', kwargs={'pk': obj.pk}), activity_stream=self.reverse('api:o_auth2_application_activity_stream_list', kwargs={'pk': obj.pk}),
) )
) )
if obj.organization_id:
res.update(
dict(
organization=self.reverse('api:organization_detail', kwargs={'pk': obj.organization_id}),
)
)
return res return res
def get_modified(self, obj): def get_modified(self, obj):
@@ -1718,7 +1724,7 @@ class InventorySerializer(LabelsListMixin, BaseSerializerWithVariables):
def validate_host_filter(self, host_filter): def validate_host_filter(self, host_filter):
if host_filter: if host_filter:
try: try:
for match in JSONBField.get_lookups().keys(): for match in models.JSONField.get_lookups().keys():
if match == 'exact': if match == 'exact':
# __exact is allowed # __exact is allowed
continue continue
@@ -1847,11 +1853,11 @@ class HostSerializer(BaseSerializerWithVariables):
if port < 1 or port > 65535: if port < 1 or port > 65535:
raise ValueError raise ValueError
except ValueError: except ValueError:
raise serializers.ValidationError(_(u'Invalid port specification: %s') % force_text(port)) raise serializers.ValidationError(_(u'Invalid port specification: %s') % force_str(port))
return name, port return name, port
def validate_name(self, value): def validate_name(self, value):
name = force_text(value or '') name = force_str(value or '')
# Validate here only, update in main validate method. # Validate here only, update in main validate method.
host, port = self._get_host_port_from_name(name) host, port = self._get_host_port_from_name(name)
return value return value
@@ -1865,13 +1871,13 @@ class HostSerializer(BaseSerializerWithVariables):
return vars_validate_or_raise(value) return vars_validate_or_raise(value)
def validate(self, attrs): def validate(self, attrs):
name = force_text(attrs.get('name', self.instance and self.instance.name or '')) name = force_str(attrs.get('name', self.instance and self.instance.name or ''))
inventory = attrs.get('inventory', self.instance and self.instance.inventory or '') inventory = attrs.get('inventory', self.instance and self.instance.inventory or '')
host, port = self._get_host_port_from_name(name) host, port = self._get_host_port_from_name(name)
if port: if port:
attrs['name'] = host attrs['name'] = host
variables = force_text(attrs.get('variables', self.instance and self.instance.variables or '')) variables = force_str(attrs.get('variables', self.instance and self.instance.variables or ''))
vars_dict = parse_yaml_or_json(variables) vars_dict = parse_yaml_or_json(variables)
vars_dict['ansible_ssh_port'] = port vars_dict['ansible_ssh_port'] = port
attrs['variables'] = json.dumps(vars_dict) attrs['variables'] = json.dumps(vars_dict)
@@ -1944,7 +1950,7 @@ class GroupSerializer(BaseSerializerWithVariables):
return res return res
def validate(self, attrs): def validate(self, attrs):
name = force_text(attrs.get('name', self.instance and self.instance.name or '')) name = force_str(attrs.get('name', self.instance and self.instance.name or ''))
inventory = attrs.get('inventory', self.instance and self.instance.inventory or '') inventory = attrs.get('inventory', self.instance and self.instance.inventory or '')
if Host.objects.filter(name=name, inventory=inventory).exists(): if Host.objects.filter(name=name, inventory=inventory).exists():
raise serializers.ValidationError(_('A Host with that name already exists.')) raise serializers.ValidationError(_('A Host with that name already exists.'))
@@ -2838,8 +2844,8 @@ class JobOptionsSerializer(LabelsListMixin, BaseSerializer):
if not project: if not project:
raise serializers.ValidationError({'project': _('This field is required.')}) raise serializers.ValidationError({'project': _('This field is required.')})
playbook_not_found = bool( playbook_not_found = bool(
(project and project.scm_type and (not project.allow_override) and playbook and force_text(playbook) not in project.playbook_files) (project and project.scm_type and (not project.allow_override) and playbook and force_str(playbook) not in project.playbook_files)
or (project and not project.scm_type and playbook and force_text(playbook) not in project.playbooks) # manual or (project and not project.scm_type and playbook and force_str(playbook) not in project.playbooks) # manual
) )
if playbook_not_found: if playbook_not_found:
raise serializers.ValidationError({'playbook': _('Playbook not found for project.')}) raise serializers.ValidationError({'playbook': _('Playbook not found for project.')})
@@ -3628,7 +3634,7 @@ class LaunchConfigurationBaseSerializer(BaseSerializer):
job_tags = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None) job_tags = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
limit = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None) limit = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
skip_tags = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None) skip_tags = serializers.CharField(allow_blank=True, allow_null=True, required=False, default=None)
diff_mode = serializers.NullBooleanField(required=False, default=None) diff_mode = serializers.BooleanField(required=False, allow_null=True, default=None)
verbosity = serializers.ChoiceField(allow_null=True, required=False, default=None, choices=VERBOSITY_CHOICES) verbosity = serializers.ChoiceField(allow_null=True, required=False, default=None, choices=VERBOSITY_CHOICES)
exclude_errors = () exclude_errors = ()
@@ -4850,6 +4856,11 @@ class InstanceSerializer(BaseSerializer):
else: else:
return float("{0:.2f}".format(((float(obj.capacity) - float(obj.consumed_capacity)) / (float(obj.capacity))) * 100)) return float("{0:.2f}".format(((float(obj.capacity) - float(obj.consumed_capacity)) / (float(obj.capacity))) * 100))
def validate(self, attrs):
if self.instance.node_type == 'hop':
raise serializers.ValidationError(_('Hop node instances may not be changed.'))
return attrs
class InstanceHealthCheckSerializer(BaseSerializer): class InstanceHealthCheckSerializer(BaseSerializer):
class Meta: class Meta:
@@ -4936,6 +4947,9 @@ class InstanceGroupSerializer(BaseSerializer):
return res return res
def validate_policy_instance_list(self, value): def validate_policy_instance_list(self, value):
if self.instance and self.instance.name in [settings.DEFAULT_EXECUTION_QUEUE_NAME, settings.DEFAULT_CONTROL_PLANE_QUEUE_NAME]:
if self.instance.policy_instance_list != value:
raise serializers.ValidationError(_('%s instance group policy_instance_list may not be changed.' % self.instance.name))
for instance_name in value: for instance_name in value:
if value.count(instance_name) > 1: if value.count(instance_name) > 1:
raise serializers.ValidationError(_('Duplicate entry {}.').format(instance_name)) raise serializers.ValidationError(_('Duplicate entry {}.').format(instance_name))
@@ -4946,6 +4960,11 @@ class InstanceGroupSerializer(BaseSerializer):
return value return value
def validate_policy_instance_percentage(self, value): def validate_policy_instance_percentage(self, value):
if self.instance and self.instance.name in [settings.DEFAULT_EXECUTION_QUEUE_NAME, settings.DEFAULT_CONTROL_PLANE_QUEUE_NAME]:
if value != self.instance.policy_instance_percentage:
raise serializers.ValidationError(
_('%s instance group policy_instance_percentage may not be changed from the initial value set by the installer.' % self.instance.name)
)
if value and self.instance and self.instance.is_container_group: if value and self.instance and self.instance.is_container_group:
raise serializers.ValidationError(_('Containerized instances may not be managed via the API')) raise serializers.ValidationError(_('Containerized instances may not be managed via the API'))
return value return value
@@ -4964,6 +4983,13 @@ class InstanceGroupSerializer(BaseSerializer):
return value return value
def validate_is_container_group(self, value):
if self.instance and self.instance.name in [settings.DEFAULT_EXECUTION_QUEUE_NAME, settings.DEFAULT_CONTROL_PLANE_QUEUE_NAME]:
if value != self.instance.is_container_group:
raise serializers.ValidationError(_('%s instance group is_container_group may not be changed.' % self.instance.name))
return value
def validate_credential(self, value): def validate_credential(self, value):
if value and not value.kubernetes: if value and not value.kubernetes:
raise serializers.ValidationError(_('Only Kubernetes credentials can be associated with an Instance Group')) raise serializers.ValidationError(_('Only Kubernetes credentials can be associated with an Instance Group'))
@@ -5078,7 +5104,7 @@ class ActivityStreamSerializer(BaseSerializer):
try: try:
return json.loads(obj.changes) return json.loads(obj.changes)
except Exception: except Exception:
logger.warn("Error deserializing activity stream json changes") logger.warning("Error deserializing activity stream json changes")
return {} return {}
def get_object_association(self, obj): def get_object_association(self, obj):

View File

@@ -1,14 +1,14 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ActivityStreamList, ActivityStreamDetail from awx.api.views import ActivityStreamList, ActivityStreamDetail
urls = [ urls = [
url(r'^$', ActivityStreamList.as_view(), name='activity_stream_list'), re_path(r'^$', ActivityStreamList.as_view(), name='activity_stream_list'),
url(r'^(?P<pk>[0-9]+)/$', ActivityStreamDetail.as_view(), name='activity_stream_detail'), re_path(r'^(?P<pk>[0-9]+)/$', ActivityStreamDetail.as_view(), name='activity_stream_detail'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
AdHocCommandList, AdHocCommandList,
@@ -16,14 +16,14 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', AdHocCommandList.as_view(), name='ad_hoc_command_list'), re_path(r'^$', AdHocCommandList.as_view(), name='ad_hoc_command_list'),
url(r'^(?P<pk>[0-9]+)/$', AdHocCommandDetail.as_view(), name='ad_hoc_command_detail'), re_path(r'^(?P<pk>[0-9]+)/$', AdHocCommandDetail.as_view(), name='ad_hoc_command_detail'),
url(r'^(?P<pk>[0-9]+)/cancel/$', AdHocCommandCancel.as_view(), name='ad_hoc_command_cancel'), re_path(r'^(?P<pk>[0-9]+)/cancel/$', AdHocCommandCancel.as_view(), name='ad_hoc_command_cancel'),
url(r'^(?P<pk>[0-9]+)/relaunch/$', AdHocCommandRelaunch.as_view(), name='ad_hoc_command_relaunch'), re_path(r'^(?P<pk>[0-9]+)/relaunch/$', AdHocCommandRelaunch.as_view(), name='ad_hoc_command_relaunch'),
url(r'^(?P<pk>[0-9]+)/events/$', AdHocCommandAdHocCommandEventsList.as_view(), name='ad_hoc_command_ad_hoc_command_events_list'), re_path(r'^(?P<pk>[0-9]+)/events/$', AdHocCommandAdHocCommandEventsList.as_view(), name='ad_hoc_command_ad_hoc_command_events_list'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', AdHocCommandActivityStreamList.as_view(), name='ad_hoc_command_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', AdHocCommandActivityStreamList.as_view(), name='ad_hoc_command_activity_stream_list'),
url(r'^(?P<pk>[0-9]+)/notifications/$', AdHocCommandNotificationsList.as_view(), name='ad_hoc_command_notifications_list'), re_path(r'^(?P<pk>[0-9]+)/notifications/$', AdHocCommandNotificationsList.as_view(), name='ad_hoc_command_notifications_list'),
url(r'^(?P<pk>[0-9]+)/stdout/$', AdHocCommandStdout.as_view(), name='ad_hoc_command_stdout'), re_path(r'^(?P<pk>[0-9]+)/stdout/$', AdHocCommandStdout.as_view(), name='ad_hoc_command_stdout'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,13 +1,13 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import AdHocCommandEventDetail from awx.api.views import AdHocCommandEventDetail
urls = [ urls = [
url(r'^(?P<pk>[0-9]+)/$', AdHocCommandEventDetail.as_view(), name='ad_hoc_command_event_detail'), re_path(r'^(?P<pk>[0-9]+)/$', AdHocCommandEventDetail.as_view(), name='ad_hoc_command_event_detail'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
CredentialList, CredentialList,
@@ -18,16 +18,16 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', CredentialList.as_view(), name='credential_list'), re_path(r'^$', CredentialList.as_view(), name='credential_list'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', CredentialActivityStreamList.as_view(), name='credential_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', CredentialActivityStreamList.as_view(), name='credential_activity_stream_list'),
url(r'^(?P<pk>[0-9]+)/$', CredentialDetail.as_view(), name='credential_detail'), re_path(r'^(?P<pk>[0-9]+)/$', CredentialDetail.as_view(), name='credential_detail'),
url(r'^(?P<pk>[0-9]+)/access_list/$', CredentialAccessList.as_view(), name='credential_access_list'), re_path(r'^(?P<pk>[0-9]+)/access_list/$', CredentialAccessList.as_view(), name='credential_access_list'),
url(r'^(?P<pk>[0-9]+)/object_roles/$', CredentialObjectRolesList.as_view(), name='credential_object_roles_list'), re_path(r'^(?P<pk>[0-9]+)/object_roles/$', CredentialObjectRolesList.as_view(), name='credential_object_roles_list'),
url(r'^(?P<pk>[0-9]+)/owner_users/$', CredentialOwnerUsersList.as_view(), name='credential_owner_users_list'), re_path(r'^(?P<pk>[0-9]+)/owner_users/$', CredentialOwnerUsersList.as_view(), name='credential_owner_users_list'),
url(r'^(?P<pk>[0-9]+)/owner_teams/$', CredentialOwnerTeamsList.as_view(), name='credential_owner_teams_list'), re_path(r'^(?P<pk>[0-9]+)/owner_teams/$', CredentialOwnerTeamsList.as_view(), name='credential_owner_teams_list'),
url(r'^(?P<pk>[0-9]+)/copy/$', CredentialCopy.as_view(), name='credential_copy'), re_path(r'^(?P<pk>[0-9]+)/copy/$', CredentialCopy.as_view(), name='credential_copy'),
url(r'^(?P<pk>[0-9]+)/input_sources/$', CredentialInputSourceSubList.as_view(), name='credential_input_source_sublist'), re_path(r'^(?P<pk>[0-9]+)/input_sources/$', CredentialInputSourceSubList.as_view(), name='credential_input_source_sublist'),
url(r'^(?P<pk>[0-9]+)/test/$', CredentialExternalTest.as_view(), name='credential_external_test'), re_path(r'^(?P<pk>[0-9]+)/test/$', CredentialExternalTest.as_view(), name='credential_external_test'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,14 +1,14 @@
# Copyright (c) 2019 Ansible, Inc. # Copyright (c) 2019 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import CredentialInputSourceDetail, CredentialInputSourceList from awx.api.views import CredentialInputSourceDetail, CredentialInputSourceList
urls = [ urls = [
url(r'^$', CredentialInputSourceList.as_view(), name='credential_input_source_list'), re_path(r'^$', CredentialInputSourceList.as_view(), name='credential_input_source_list'),
url(r'^(?P<pk>[0-9]+)/$', CredentialInputSourceDetail.as_view(), name='credential_input_source_detail'), re_path(r'^(?P<pk>[0-9]+)/$', CredentialInputSourceDetail.as_view(), name='credential_input_source_detail'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,17 +1,17 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import CredentialTypeList, CredentialTypeDetail, CredentialTypeCredentialList, CredentialTypeActivityStreamList, CredentialTypeExternalTest from awx.api.views import CredentialTypeList, CredentialTypeDetail, CredentialTypeCredentialList, CredentialTypeActivityStreamList, CredentialTypeExternalTest
urls = [ urls = [
url(r'^$', CredentialTypeList.as_view(), name='credential_type_list'), re_path(r'^$', CredentialTypeList.as_view(), name='credential_type_list'),
url(r'^(?P<pk>[0-9]+)/$', CredentialTypeDetail.as_view(), name='credential_type_detail'), re_path(r'^(?P<pk>[0-9]+)/$', CredentialTypeDetail.as_view(), name='credential_type_detail'),
url(r'^(?P<pk>[0-9]+)/credentials/$', CredentialTypeCredentialList.as_view(), name='credential_type_credential_list'), re_path(r'^(?P<pk>[0-9]+)/credentials/$', CredentialTypeCredentialList.as_view(), name='credential_type_credential_list'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', CredentialTypeActivityStreamList.as_view(), name='credential_type_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', CredentialTypeActivityStreamList.as_view(), name='credential_type_activity_stream_list'),
url(r'^(?P<pk>[0-9]+)/test/$', CredentialTypeExternalTest.as_view(), name='credential_type_external_test'), re_path(r'^(?P<pk>[0-9]+)/test/$', CredentialTypeExternalTest.as_view(), name='credential_type_external_test'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,4 +1,4 @@
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
ExecutionEnvironmentList, ExecutionEnvironmentList,
@@ -10,11 +10,11 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', ExecutionEnvironmentList.as_view(), name='execution_environment_list'), re_path(r'^$', ExecutionEnvironmentList.as_view(), name='execution_environment_list'),
url(r'^(?P<pk>[0-9]+)/$', ExecutionEnvironmentDetail.as_view(), name='execution_environment_detail'), re_path(r'^(?P<pk>[0-9]+)/$', ExecutionEnvironmentDetail.as_view(), name='execution_environment_detail'),
url(r'^(?P<pk>[0-9]+)/unified_job_templates/$', ExecutionEnvironmentJobTemplateList.as_view(), name='execution_environment_job_template_list'), re_path(r'^(?P<pk>[0-9]+)/unified_job_templates/$', ExecutionEnvironmentJobTemplateList.as_view(), name='execution_environment_job_template_list'),
url(r'^(?P<pk>[0-9]+)/copy/$', ExecutionEnvironmentCopy.as_view(), name='execution_environment_copy'), re_path(r'^(?P<pk>[0-9]+)/copy/$', ExecutionEnvironmentCopy.as_view(), name='execution_environment_copy'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', ExecutionEnvironmentActivityStreamList.as_view(), name='execution_environment_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', ExecutionEnvironmentActivityStreamList.as_view(), name='execution_environment_activity_stream_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
GroupList, GroupList,
@@ -20,18 +20,18 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', GroupList.as_view(), name='group_list'), re_path(r'^$', GroupList.as_view(), name='group_list'),
url(r'^(?P<pk>[0-9]+)/$', GroupDetail.as_view(), name='group_detail'), re_path(r'^(?P<pk>[0-9]+)/$', GroupDetail.as_view(), name='group_detail'),
url(r'^(?P<pk>[0-9]+)/children/$', GroupChildrenList.as_view(), name='group_children_list'), re_path(r'^(?P<pk>[0-9]+)/children/$', GroupChildrenList.as_view(), name='group_children_list'),
url(r'^(?P<pk>[0-9]+)/hosts/$', GroupHostsList.as_view(), name='group_hosts_list'), re_path(r'^(?P<pk>[0-9]+)/hosts/$', GroupHostsList.as_view(), name='group_hosts_list'),
url(r'^(?P<pk>[0-9]+)/all_hosts/$', GroupAllHostsList.as_view(), name='group_all_hosts_list'), re_path(r'^(?P<pk>[0-9]+)/all_hosts/$', GroupAllHostsList.as_view(), name='group_all_hosts_list'),
url(r'^(?P<pk>[0-9]+)/variable_data/$', GroupVariableData.as_view(), name='group_variable_data'), re_path(r'^(?P<pk>[0-9]+)/variable_data/$', GroupVariableData.as_view(), name='group_variable_data'),
url(r'^(?P<pk>[0-9]+)/job_events/$', GroupJobEventsList.as_view(), name='group_job_events_list'), re_path(r'^(?P<pk>[0-9]+)/job_events/$', GroupJobEventsList.as_view(), name='group_job_events_list'),
url(r'^(?P<pk>[0-9]+)/job_host_summaries/$', GroupJobHostSummariesList.as_view(), name='group_job_host_summaries_list'), re_path(r'^(?P<pk>[0-9]+)/job_host_summaries/$', GroupJobHostSummariesList.as_view(), name='group_job_host_summaries_list'),
url(r'^(?P<pk>[0-9]+)/potential_children/$', GroupPotentialChildrenList.as_view(), name='group_potential_children_list'), re_path(r'^(?P<pk>[0-9]+)/potential_children/$', GroupPotentialChildrenList.as_view(), name='group_potential_children_list'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', GroupActivityStreamList.as_view(), name='group_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', GroupActivityStreamList.as_view(), name='group_activity_stream_list'),
url(r'^(?P<pk>[0-9]+)/inventory_sources/$', GroupInventorySourcesList.as_view(), name='group_inventory_sources_list'), re_path(r'^(?P<pk>[0-9]+)/inventory_sources/$', GroupInventorySourcesList.as_view(), name='group_inventory_sources_list'),
url(r'^(?P<pk>[0-9]+)/ad_hoc_commands/$', GroupAdHocCommandsList.as_view(), name='group_ad_hoc_commands_list'), re_path(r'^(?P<pk>[0-9]+)/ad_hoc_commands/$', GroupAdHocCommandsList.as_view(), name='group_ad_hoc_commands_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
HostList, HostList,
@@ -20,18 +20,18 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', HostList.as_view(), name='host_list'), re_path(r'^$', HostList.as_view(), name='host_list'),
url(r'^(?P<pk>[0-9]+)/$', HostDetail.as_view(), name='host_detail'), re_path(r'^(?P<pk>[0-9]+)/$', HostDetail.as_view(), name='host_detail'),
url(r'^(?P<pk>[0-9]+)/variable_data/$', HostVariableData.as_view(), name='host_variable_data'), re_path(r'^(?P<pk>[0-9]+)/variable_data/$', HostVariableData.as_view(), name='host_variable_data'),
url(r'^(?P<pk>[0-9]+)/groups/$', HostGroupsList.as_view(), name='host_groups_list'), re_path(r'^(?P<pk>[0-9]+)/groups/$', HostGroupsList.as_view(), name='host_groups_list'),
url(r'^(?P<pk>[0-9]+)/all_groups/$', HostAllGroupsList.as_view(), name='host_all_groups_list'), re_path(r'^(?P<pk>[0-9]+)/all_groups/$', HostAllGroupsList.as_view(), name='host_all_groups_list'),
url(r'^(?P<pk>[0-9]+)/job_events/', HostJobEventsList.as_view(), name='host_job_events_list'), re_path(r'^(?P<pk>[0-9]+)/job_events/', HostJobEventsList.as_view(), name='host_job_events_list'),
url(r'^(?P<pk>[0-9]+)/job_host_summaries/$', HostJobHostSummariesList.as_view(), name='host_job_host_summaries_list'), re_path(r'^(?P<pk>[0-9]+)/job_host_summaries/$', HostJobHostSummariesList.as_view(), name='host_job_host_summaries_list'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', HostActivityStreamList.as_view(), name='host_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', HostActivityStreamList.as_view(), name='host_activity_stream_list'),
url(r'^(?P<pk>[0-9]+)/inventory_sources/$', HostInventorySourcesList.as_view(), name='host_inventory_sources_list'), re_path(r'^(?P<pk>[0-9]+)/inventory_sources/$', HostInventorySourcesList.as_view(), name='host_inventory_sources_list'),
url(r'^(?P<pk>[0-9]+)/smart_inventories/$', HostSmartInventoriesList.as_view(), name='host_smart_inventories_list'), re_path(r'^(?P<pk>[0-9]+)/smart_inventories/$', HostSmartInventoriesList.as_view(), name='host_smart_inventories_list'),
url(r'^(?P<pk>[0-9]+)/ad_hoc_commands/$', HostAdHocCommandsList.as_view(), name='host_ad_hoc_commands_list'), re_path(r'^(?P<pk>[0-9]+)/ad_hoc_commands/$', HostAdHocCommandsList.as_view(), name='host_ad_hoc_commands_list'),
url(r'^(?P<pk>[0-9]+)/ad_hoc_command_events/$', HostAdHocCommandEventsList.as_view(), name='host_ad_hoc_command_events_list'), re_path(r'^(?P<pk>[0-9]+)/ad_hoc_command_events/$', HostAdHocCommandEventsList.as_view(), name='host_ad_hoc_command_events_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,17 +1,17 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import InstanceList, InstanceDetail, InstanceUnifiedJobsList, InstanceInstanceGroupsList, InstanceHealthCheck from awx.api.views import InstanceList, InstanceDetail, InstanceUnifiedJobsList, InstanceInstanceGroupsList, InstanceHealthCheck
urls = [ urls = [
url(r'^$', InstanceList.as_view(), name='instance_list'), re_path(r'^$', InstanceList.as_view(), name='instance_list'),
url(r'^(?P<pk>[0-9]+)/$', InstanceDetail.as_view(), name='instance_detail'), re_path(r'^(?P<pk>[0-9]+)/$', InstanceDetail.as_view(), name='instance_detail'),
url(r'^(?P<pk>[0-9]+)/jobs/$', InstanceUnifiedJobsList.as_view(), name='instance_unified_jobs_list'), re_path(r'^(?P<pk>[0-9]+)/jobs/$', InstanceUnifiedJobsList.as_view(), name='instance_unified_jobs_list'),
url(r'^(?P<pk>[0-9]+)/instance_groups/$', InstanceInstanceGroupsList.as_view(), name='instance_instance_groups_list'), re_path(r'^(?P<pk>[0-9]+)/instance_groups/$', InstanceInstanceGroupsList.as_view(), name='instance_instance_groups_list'),
url(r'^(?P<pk>[0-9]+)/health_check/$', InstanceHealthCheck.as_view(), name='instance_health_check'), re_path(r'^(?P<pk>[0-9]+)/health_check/$', InstanceHealthCheck.as_view(), name='instance_health_check'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,16 +1,16 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import InstanceGroupList, InstanceGroupDetail, InstanceGroupUnifiedJobsList, InstanceGroupInstanceList from awx.api.views import InstanceGroupList, InstanceGroupDetail, InstanceGroupUnifiedJobsList, InstanceGroupInstanceList
urls = [ urls = [
url(r'^$', InstanceGroupList.as_view(), name='instance_group_list'), re_path(r'^$', InstanceGroupList.as_view(), name='instance_group_list'),
url(r'^(?P<pk>[0-9]+)/$', InstanceGroupDetail.as_view(), name='instance_group_detail'), re_path(r'^(?P<pk>[0-9]+)/$', InstanceGroupDetail.as_view(), name='instance_group_detail'),
url(r'^(?P<pk>[0-9]+)/jobs/$', InstanceGroupUnifiedJobsList.as_view(), name='instance_group_unified_jobs_list'), re_path(r'^(?P<pk>[0-9]+)/jobs/$', InstanceGroupUnifiedJobsList.as_view(), name='instance_group_unified_jobs_list'),
url(r'^(?P<pk>[0-9]+)/instances/$', InstanceGroupInstanceList.as_view(), name='instance_group_instance_list'), re_path(r'^(?P<pk>[0-9]+)/instances/$', InstanceGroupInstanceList.as_view(), name='instance_group_instance_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
InventoryList, InventoryList,
@@ -26,24 +26,24 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', InventoryList.as_view(), name='inventory_list'), re_path(r'^$', InventoryList.as_view(), name='inventory_list'),
url(r'^(?P<pk>[0-9]+)/$', InventoryDetail.as_view(), name='inventory_detail'), re_path(r'^(?P<pk>[0-9]+)/$', InventoryDetail.as_view(), name='inventory_detail'),
url(r'^(?P<pk>[0-9]+)/hosts/$', InventoryHostsList.as_view(), name='inventory_hosts_list'), re_path(r'^(?P<pk>[0-9]+)/hosts/$', InventoryHostsList.as_view(), name='inventory_hosts_list'),
url(r'^(?P<pk>[0-9]+)/groups/$', InventoryGroupsList.as_view(), name='inventory_groups_list'), re_path(r'^(?P<pk>[0-9]+)/groups/$', InventoryGroupsList.as_view(), name='inventory_groups_list'),
url(r'^(?P<pk>[0-9]+)/root_groups/$', InventoryRootGroupsList.as_view(), name='inventory_root_groups_list'), re_path(r'^(?P<pk>[0-9]+)/root_groups/$', InventoryRootGroupsList.as_view(), name='inventory_root_groups_list'),
url(r'^(?P<pk>[0-9]+)/variable_data/$', InventoryVariableData.as_view(), name='inventory_variable_data'), re_path(r'^(?P<pk>[0-9]+)/variable_data/$', InventoryVariableData.as_view(), name='inventory_variable_data'),
url(r'^(?P<pk>[0-9]+)/script/$', InventoryScriptView.as_view(), name='inventory_script_view'), re_path(r'^(?P<pk>[0-9]+)/script/$', InventoryScriptView.as_view(), name='inventory_script_view'),
url(r'^(?P<pk>[0-9]+)/tree/$', InventoryTreeView.as_view(), name='inventory_tree_view'), re_path(r'^(?P<pk>[0-9]+)/tree/$', InventoryTreeView.as_view(), name='inventory_tree_view'),
url(r'^(?P<pk>[0-9]+)/inventory_sources/$', InventoryInventorySourcesList.as_view(), name='inventory_inventory_sources_list'), re_path(r'^(?P<pk>[0-9]+)/inventory_sources/$', InventoryInventorySourcesList.as_view(), name='inventory_inventory_sources_list'),
url(r'^(?P<pk>[0-9]+)/update_inventory_sources/$', InventoryInventorySourcesUpdate.as_view(), name='inventory_inventory_sources_update'), re_path(r'^(?P<pk>[0-9]+)/update_inventory_sources/$', InventoryInventorySourcesUpdate.as_view(), name='inventory_inventory_sources_update'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', InventoryActivityStreamList.as_view(), name='inventory_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', InventoryActivityStreamList.as_view(), name='inventory_activity_stream_list'),
url(r'^(?P<pk>[0-9]+)/job_templates/$', InventoryJobTemplateList.as_view(), name='inventory_job_template_list'), re_path(r'^(?P<pk>[0-9]+)/job_templates/$', InventoryJobTemplateList.as_view(), name='inventory_job_template_list'),
url(r'^(?P<pk>[0-9]+)/ad_hoc_commands/$', InventoryAdHocCommandsList.as_view(), name='inventory_ad_hoc_commands_list'), re_path(r'^(?P<pk>[0-9]+)/ad_hoc_commands/$', InventoryAdHocCommandsList.as_view(), name='inventory_ad_hoc_commands_list'),
url(r'^(?P<pk>[0-9]+)/access_list/$', InventoryAccessList.as_view(), name='inventory_access_list'), re_path(r'^(?P<pk>[0-9]+)/access_list/$', InventoryAccessList.as_view(), name='inventory_access_list'),
url(r'^(?P<pk>[0-9]+)/object_roles/$', InventoryObjectRolesList.as_view(), name='inventory_object_roles_list'), re_path(r'^(?P<pk>[0-9]+)/object_roles/$', InventoryObjectRolesList.as_view(), name='inventory_object_roles_list'),
url(r'^(?P<pk>[0-9]+)/instance_groups/$', InventoryInstanceGroupsList.as_view(), name='inventory_instance_groups_list'), re_path(r'^(?P<pk>[0-9]+)/instance_groups/$', InventoryInstanceGroupsList.as_view(), name='inventory_instance_groups_list'),
url(r'^(?P<pk>[0-9]+)/labels/$', InventoryLabelList.as_view(), name='inventory_label_list'), re_path(r'^(?P<pk>[0-9]+)/labels/$', InventoryLabelList.as_view(), name='inventory_label_list'),
url(r'^(?P<pk>[0-9]+)/copy/$', InventoryCopy.as_view(), name='inventory_copy'), re_path(r'^(?P<pk>[0-9]+)/copy/$', InventoryCopy.as_view(), name='inventory_copy'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
InventorySourceList, InventorySourceList,
@@ -20,26 +20,26 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', InventorySourceList.as_view(), name='inventory_source_list'), re_path(r'^$', InventorySourceList.as_view(), name='inventory_source_list'),
url(r'^(?P<pk>[0-9]+)/$', InventorySourceDetail.as_view(), name='inventory_source_detail'), re_path(r'^(?P<pk>[0-9]+)/$', InventorySourceDetail.as_view(), name='inventory_source_detail'),
url(r'^(?P<pk>[0-9]+)/update/$', InventorySourceUpdateView.as_view(), name='inventory_source_update_view'), re_path(r'^(?P<pk>[0-9]+)/update/$', InventorySourceUpdateView.as_view(), name='inventory_source_update_view'),
url(r'^(?P<pk>[0-9]+)/inventory_updates/$', InventorySourceUpdatesList.as_view(), name='inventory_source_updates_list'), re_path(r'^(?P<pk>[0-9]+)/inventory_updates/$', InventorySourceUpdatesList.as_view(), name='inventory_source_updates_list'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', InventorySourceActivityStreamList.as_view(), name='inventory_source_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', InventorySourceActivityStreamList.as_view(), name='inventory_source_activity_stream_list'),
url(r'^(?P<pk>[0-9]+)/schedules/$', InventorySourceSchedulesList.as_view(), name='inventory_source_schedules_list'), re_path(r'^(?P<pk>[0-9]+)/schedules/$', InventorySourceSchedulesList.as_view(), name='inventory_source_schedules_list'),
url(r'^(?P<pk>[0-9]+)/credentials/$', InventorySourceCredentialsList.as_view(), name='inventory_source_credentials_list'), re_path(r'^(?P<pk>[0-9]+)/credentials/$', InventorySourceCredentialsList.as_view(), name='inventory_source_credentials_list'),
url(r'^(?P<pk>[0-9]+)/groups/$', InventorySourceGroupsList.as_view(), name='inventory_source_groups_list'), re_path(r'^(?P<pk>[0-9]+)/groups/$', InventorySourceGroupsList.as_view(), name='inventory_source_groups_list'),
url(r'^(?P<pk>[0-9]+)/hosts/$', InventorySourceHostsList.as_view(), name='inventory_source_hosts_list'), re_path(r'^(?P<pk>[0-9]+)/hosts/$', InventorySourceHostsList.as_view(), name='inventory_source_hosts_list'),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_started/$', r'^(?P<pk>[0-9]+)/notification_templates_started/$',
InventorySourceNotificationTemplatesStartedList.as_view(), InventorySourceNotificationTemplatesStartedList.as_view(),
name='inventory_source_notification_templates_started_list', name='inventory_source_notification_templates_started_list',
), ),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_error/$', r'^(?P<pk>[0-9]+)/notification_templates_error/$',
InventorySourceNotificationTemplatesErrorList.as_view(), InventorySourceNotificationTemplatesErrorList.as_view(),
name='inventory_source_notification_templates_error_list', name='inventory_source_notification_templates_error_list',
), ),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_success/$', r'^(?P<pk>[0-9]+)/notification_templates_success/$',
InventorySourceNotificationTemplatesSuccessList.as_view(), InventorySourceNotificationTemplatesSuccessList.as_view(),
name='inventory_source_notification_templates_success_list', name='inventory_source_notification_templates_success_list',

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
InventoryUpdateList, InventoryUpdateList,
@@ -15,13 +15,13 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', InventoryUpdateList.as_view(), name='inventory_update_list'), re_path(r'^$', InventoryUpdateList.as_view(), name='inventory_update_list'),
url(r'^(?P<pk>[0-9]+)/$', InventoryUpdateDetail.as_view(), name='inventory_update_detail'), re_path(r'^(?P<pk>[0-9]+)/$', InventoryUpdateDetail.as_view(), name='inventory_update_detail'),
url(r'^(?P<pk>[0-9]+)/cancel/$', InventoryUpdateCancel.as_view(), name='inventory_update_cancel'), re_path(r'^(?P<pk>[0-9]+)/cancel/$', InventoryUpdateCancel.as_view(), name='inventory_update_cancel'),
url(r'^(?P<pk>[0-9]+)/stdout/$', InventoryUpdateStdout.as_view(), name='inventory_update_stdout'), re_path(r'^(?P<pk>[0-9]+)/stdout/$', InventoryUpdateStdout.as_view(), name='inventory_update_stdout'),
url(r'^(?P<pk>[0-9]+)/notifications/$', InventoryUpdateNotificationsList.as_view(), name='inventory_update_notifications_list'), re_path(r'^(?P<pk>[0-9]+)/notifications/$', InventoryUpdateNotificationsList.as_view(), name='inventory_update_notifications_list'),
url(r'^(?P<pk>[0-9]+)/credentials/$', InventoryUpdateCredentialsList.as_view(), name='inventory_update_credentials_list'), re_path(r'^(?P<pk>[0-9]+)/credentials/$', InventoryUpdateCredentialsList.as_view(), name='inventory_update_credentials_list'),
url(r'^(?P<pk>[0-9]+)/events/$', InventoryUpdateEventsList.as_view(), name='inventory_update_events_list'), re_path(r'^(?P<pk>[0-9]+)/events/$', InventoryUpdateEventsList.as_view(), name='inventory_update_events_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
JobList, JobList,
@@ -20,18 +20,18 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', JobList.as_view(), name='job_list'), re_path(r'^$', JobList.as_view(), name='job_list'),
url(r'^(?P<pk>[0-9]+)/$', JobDetail.as_view(), name='job_detail'), re_path(r'^(?P<pk>[0-9]+)/$', JobDetail.as_view(), name='job_detail'),
url(r'^(?P<pk>[0-9]+)/cancel/$', JobCancel.as_view(), name='job_cancel'), re_path(r'^(?P<pk>[0-9]+)/cancel/$', JobCancel.as_view(), name='job_cancel'),
url(r'^(?P<pk>[0-9]+)/relaunch/$', JobRelaunch.as_view(), name='job_relaunch'), re_path(r'^(?P<pk>[0-9]+)/relaunch/$', JobRelaunch.as_view(), name='job_relaunch'),
url(r'^(?P<pk>[0-9]+)/create_schedule/$', JobCreateSchedule.as_view(), name='job_create_schedule'), re_path(r'^(?P<pk>[0-9]+)/create_schedule/$', JobCreateSchedule.as_view(), name='job_create_schedule'),
url(r'^(?P<pk>[0-9]+)/job_host_summaries/$', JobJobHostSummariesList.as_view(), name='job_job_host_summaries_list'), re_path(r'^(?P<pk>[0-9]+)/job_host_summaries/$', JobJobHostSummariesList.as_view(), name='job_job_host_summaries_list'),
url(r'^(?P<pk>[0-9]+)/job_events/$', JobJobEventsList.as_view(), name='job_job_events_list'), re_path(r'^(?P<pk>[0-9]+)/job_events/$', JobJobEventsList.as_view(), name='job_job_events_list'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', JobActivityStreamList.as_view(), name='job_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', JobActivityStreamList.as_view(), name='job_activity_stream_list'),
url(r'^(?P<pk>[0-9]+)/stdout/$', JobStdout.as_view(), name='job_stdout'), re_path(r'^(?P<pk>[0-9]+)/stdout/$', JobStdout.as_view(), name='job_stdout'),
url(r'^(?P<pk>[0-9]+)/notifications/$', JobNotificationsList.as_view(), name='job_notifications_list'), re_path(r'^(?P<pk>[0-9]+)/notifications/$', JobNotificationsList.as_view(), name='job_notifications_list'),
url(r'^(?P<pk>[0-9]+)/labels/$', JobLabelList.as_view(), name='job_label_list'), re_path(r'^(?P<pk>[0-9]+)/labels/$', JobLabelList.as_view(), name='job_label_list'),
url(r'^(?P<pk>[0-9]+)/$', JobHostSummaryDetail.as_view(), name='job_host_summary_detail'), re_path(r'^(?P<pk>[0-9]+)/$', JobHostSummaryDetail.as_view(), name='job_host_summary_detail'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,13 +1,13 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import JobEventDetail, JobEventChildrenList from awx.api.views import JobEventDetail, JobEventChildrenList
urls = [ urls = [
url(r'^(?P<pk>[0-9]+)/$', JobEventDetail.as_view(), name='job_event_detail'), re_path(r'^(?P<pk>[0-9]+)/$', JobEventDetail.as_view(), name='job_event_detail'),
url(r'^(?P<pk>[0-9]+)/children/$', JobEventChildrenList.as_view(), name='job_event_children_list'), re_path(r'^(?P<pk>[0-9]+)/children/$', JobEventChildrenList.as_view(), name='job_event_children_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,11 +1,11 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import JobHostSummaryDetail from awx.api.views import JobHostSummaryDetail
urls = [url(r'^(?P<pk>[0-9]+)/$', JobHostSummaryDetail.as_view(), name='job_host_summary_detail')] urls = [re_path(r'^(?P<pk>[0-9]+)/$', JobHostSummaryDetail.as_view(), name='job_host_summary_detail')]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import include, url from django.urls import include, re_path
from awx.api.views import ( from awx.api.views import (
JobTemplateList, JobTemplateList,
@@ -25,36 +25,36 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', JobTemplateList.as_view(), name='job_template_list'), re_path(r'^$', JobTemplateList.as_view(), name='job_template_list'),
url(r'^(?P<pk>[0-9]+)/$', JobTemplateDetail.as_view(), name='job_template_detail'), re_path(r'^(?P<pk>[0-9]+)/$', JobTemplateDetail.as_view(), name='job_template_detail'),
url(r'^(?P<pk>[0-9]+)/launch/$', JobTemplateLaunch.as_view(), name='job_template_launch'), re_path(r'^(?P<pk>[0-9]+)/launch/$', JobTemplateLaunch.as_view(), name='job_template_launch'),
url(r'^(?P<pk>[0-9]+)/jobs/$', JobTemplateJobsList.as_view(), name='job_template_jobs_list'), re_path(r'^(?P<pk>[0-9]+)/jobs/$', JobTemplateJobsList.as_view(), name='job_template_jobs_list'),
url(r'^(?P<pk>[0-9]+)/slice_workflow_jobs/$', JobTemplateSliceWorkflowJobsList.as_view(), name='job_template_slice_workflow_jobs_list'), re_path(r'^(?P<pk>[0-9]+)/slice_workflow_jobs/$', JobTemplateSliceWorkflowJobsList.as_view(), name='job_template_slice_workflow_jobs_list'),
url(r'^(?P<pk>[0-9]+)/callback/$', JobTemplateCallback.as_view(), name='job_template_callback'), re_path(r'^(?P<pk>[0-9]+)/callback/$', JobTemplateCallback.as_view(), name='job_template_callback'),
url(r'^(?P<pk>[0-9]+)/schedules/$', JobTemplateSchedulesList.as_view(), name='job_template_schedules_list'), re_path(r'^(?P<pk>[0-9]+)/schedules/$', JobTemplateSchedulesList.as_view(), name='job_template_schedules_list'),
url(r'^(?P<pk>[0-9]+)/survey_spec/$', JobTemplateSurveySpec.as_view(), name='job_template_survey_spec'), re_path(r'^(?P<pk>[0-9]+)/survey_spec/$', JobTemplateSurveySpec.as_view(), name='job_template_survey_spec'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', JobTemplateActivityStreamList.as_view(), name='job_template_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', JobTemplateActivityStreamList.as_view(), name='job_template_activity_stream_list'),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_started/$', r'^(?P<pk>[0-9]+)/notification_templates_started/$',
JobTemplateNotificationTemplatesStartedList.as_view(), JobTemplateNotificationTemplatesStartedList.as_view(),
name='job_template_notification_templates_started_list', name='job_template_notification_templates_started_list',
), ),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_error/$', r'^(?P<pk>[0-9]+)/notification_templates_error/$',
JobTemplateNotificationTemplatesErrorList.as_view(), JobTemplateNotificationTemplatesErrorList.as_view(),
name='job_template_notification_templates_error_list', name='job_template_notification_templates_error_list',
), ),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_success/$', r'^(?P<pk>[0-9]+)/notification_templates_success/$',
JobTemplateNotificationTemplatesSuccessList.as_view(), JobTemplateNotificationTemplatesSuccessList.as_view(),
name='job_template_notification_templates_success_list', name='job_template_notification_templates_success_list',
), ),
url(r'^(?P<pk>[0-9]+)/instance_groups/$', JobTemplateInstanceGroupsList.as_view(), name='job_template_instance_groups_list'), re_path(r'^(?P<pk>[0-9]+)/instance_groups/$', JobTemplateInstanceGroupsList.as_view(), name='job_template_instance_groups_list'),
url(r'^(?P<pk>[0-9]+)/access_list/$', JobTemplateAccessList.as_view(), name='job_template_access_list'), re_path(r'^(?P<pk>[0-9]+)/access_list/$', JobTemplateAccessList.as_view(), name='job_template_access_list'),
url(r'^(?P<pk>[0-9]+)/object_roles/$', JobTemplateObjectRolesList.as_view(), name='job_template_object_roles_list'), re_path(r'^(?P<pk>[0-9]+)/object_roles/$', JobTemplateObjectRolesList.as_view(), name='job_template_object_roles_list'),
url(r'^(?P<pk>[0-9]+)/labels/$', JobTemplateLabelList.as_view(), name='job_template_label_list'), re_path(r'^(?P<pk>[0-9]+)/labels/$', JobTemplateLabelList.as_view(), name='job_template_label_list'),
url(r'^(?P<pk>[0-9]+)/copy/$', JobTemplateCopy.as_view(), name='job_template_copy'), re_path(r'^(?P<pk>[0-9]+)/copy/$', JobTemplateCopy.as_view(), name='job_template_copy'),
url(r'^(?P<pk>[0-9]+)/', include('awx.api.urls.webhooks'), {'model_kwarg': 'job_templates'}), re_path(r'^(?P<pk>[0-9]+)/', include('awx.api.urls.webhooks'), {'model_kwarg': 'job_templates'}),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,11 +1,11 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import LabelList, LabelDetail from awx.api.views import LabelList, LabelDetail
urls = [url(r'^$', LabelList.as_view(), name='label_list'), url(r'^(?P<pk>[0-9]+)/$', LabelDetail.as_view(), name='label_detail')] urls = [re_path(r'^$', LabelList.as_view(), name='label_list'), re_path(r'^(?P<pk>[0-9]+)/$', LabelDetail.as_view(), name='label_detail')]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,11 +1,14 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import NotificationList, NotificationDetail from awx.api.views import NotificationList, NotificationDetail
urls = [url(r'^$', NotificationList.as_view(), name='notification_list'), url(r'^(?P<pk>[0-9]+)/$', NotificationDetail.as_view(), name='notification_detail')] urls = [
re_path(r'^$', NotificationList.as_view(), name='notification_list'),
re_path(r'^(?P<pk>[0-9]+)/$', NotificationDetail.as_view(), name='notification_detail'),
]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
NotificationTemplateList, NotificationTemplateList,
@@ -13,11 +13,11 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', NotificationTemplateList.as_view(), name='notification_template_list'), re_path(r'^$', NotificationTemplateList.as_view(), name='notification_template_list'),
url(r'^(?P<pk>[0-9]+)/$', NotificationTemplateDetail.as_view(), name='notification_template_detail'), re_path(r'^(?P<pk>[0-9]+)/$', NotificationTemplateDetail.as_view(), name='notification_template_detail'),
url(r'^(?P<pk>[0-9]+)/test/$', NotificationTemplateTest.as_view(), name='notification_template_test'), re_path(r'^(?P<pk>[0-9]+)/test/$', NotificationTemplateTest.as_view(), name='notification_template_test'),
url(r'^(?P<pk>[0-9]+)/notifications/$', NotificationTemplateNotificationList.as_view(), name='notification_template_notification_list'), re_path(r'^(?P<pk>[0-9]+)/notifications/$', NotificationTemplateNotificationList.as_view(), name='notification_template_notification_list'),
url(r'^(?P<pk>[0-9]+)/copy/$', NotificationTemplateCopy.as_view(), name='notification_template_copy'), re_path(r'^(?P<pk>[0-9]+)/copy/$', NotificationTemplateCopy.as_view(), name='notification_template_copy'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
OAuth2ApplicationList, OAuth2ApplicationList,
@@ -15,13 +15,13 @@ from awx.api.views import (
urls = [ urls = [
url(r'^applications/$', OAuth2ApplicationList.as_view(), name='o_auth2_application_list'), re_path(r'^applications/$', OAuth2ApplicationList.as_view(), name='o_auth2_application_list'),
url(r'^applications/(?P<pk>[0-9]+)/$', OAuth2ApplicationDetail.as_view(), name='o_auth2_application_detail'), re_path(r'^applications/(?P<pk>[0-9]+)/$', OAuth2ApplicationDetail.as_view(), name='o_auth2_application_detail'),
url(r'^applications/(?P<pk>[0-9]+)/tokens/$', ApplicationOAuth2TokenList.as_view(), name='o_auth2_application_token_list'), re_path(r'^applications/(?P<pk>[0-9]+)/tokens/$', ApplicationOAuth2TokenList.as_view(), name='o_auth2_application_token_list'),
url(r'^applications/(?P<pk>[0-9]+)/activity_stream/$', OAuth2ApplicationActivityStreamList.as_view(), name='o_auth2_application_activity_stream_list'), re_path(r'^applications/(?P<pk>[0-9]+)/activity_stream/$', OAuth2ApplicationActivityStreamList.as_view(), name='o_auth2_application_activity_stream_list'),
url(r'^tokens/$', OAuth2TokenList.as_view(), name='o_auth2_token_list'), re_path(r'^tokens/$', OAuth2TokenList.as_view(), name='o_auth2_token_list'),
url(r'^tokens/(?P<pk>[0-9]+)/$', OAuth2TokenDetail.as_view(), name='o_auth2_token_detail'), re_path(r'^tokens/(?P<pk>[0-9]+)/$', OAuth2TokenDetail.as_view(), name='o_auth2_token_detail'),
url(r'^tokens/(?P<pk>[0-9]+)/activity_stream/$', OAuth2TokenActivityStreamList.as_view(), name='o_auth2_token_activity_stream_list'), re_path(r'^tokens/(?P<pk>[0-9]+)/activity_stream/$', OAuth2TokenActivityStreamList.as_view(), name='o_auth2_token_activity_stream_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -4,7 +4,7 @@ from datetime import timedelta
from django.utils.timezone import now from django.utils.timezone import now
from django.conf import settings from django.conf import settings
from django.conf.urls import url from django.urls import re_path
from oauthlib import oauth2 from oauthlib import oauth2
from oauth2_provider import views from oauth2_provider import views
@@ -35,10 +35,10 @@ class TokenView(views.TokenView):
urls = [ urls = [
url(r'^$', ApiOAuthAuthorizationRootView.as_view(), name='oauth_authorization_root_view'), re_path(r'^$', ApiOAuthAuthorizationRootView.as_view(), name='oauth_authorization_root_view'),
url(r"^authorize/$", views.AuthorizationView.as_view(), name="authorize"), re_path(r"^authorize/$", views.AuthorizationView.as_view(), name="authorize"),
url(r"^token/$", TokenView.as_view(), name="token"), re_path(r"^token/$", TokenView.as_view(), name="token"),
url(r"^revoke_token/$", views.RevokeTokenView.as_view(), name="revoke-token"), re_path(r"^revoke_token/$", views.RevokeTokenView.as_view(), name="revoke-token"),
] ]

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
OrganizationList, OrganizationList,
@@ -30,44 +30,44 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', OrganizationList.as_view(), name='organization_list'), re_path(r'^$', OrganizationList.as_view(), name='organization_list'),
url(r'^(?P<pk>[0-9]+)/$', OrganizationDetail.as_view(), name='organization_detail'), re_path(r'^(?P<pk>[0-9]+)/$', OrganizationDetail.as_view(), name='organization_detail'),
url(r'^(?P<pk>[0-9]+)/users/$', OrganizationUsersList.as_view(), name='organization_users_list'), re_path(r'^(?P<pk>[0-9]+)/users/$', OrganizationUsersList.as_view(), name='organization_users_list'),
url(r'^(?P<pk>[0-9]+)/admins/$', OrganizationAdminsList.as_view(), name='organization_admins_list'), re_path(r'^(?P<pk>[0-9]+)/admins/$', OrganizationAdminsList.as_view(), name='organization_admins_list'),
url(r'^(?P<pk>[0-9]+)/inventories/$', OrganizationInventoriesList.as_view(), name='organization_inventories_list'), re_path(r'^(?P<pk>[0-9]+)/inventories/$', OrganizationInventoriesList.as_view(), name='organization_inventories_list'),
url(r'^(?P<pk>[0-9]+)/execution_environments/$', OrganizationExecutionEnvironmentsList.as_view(), name='organization_execution_environments_list'), re_path(r'^(?P<pk>[0-9]+)/execution_environments/$', OrganizationExecutionEnvironmentsList.as_view(), name='organization_execution_environments_list'),
url(r'^(?P<pk>[0-9]+)/projects/$', OrganizationProjectsList.as_view(), name='organization_projects_list'), re_path(r'^(?P<pk>[0-9]+)/projects/$', OrganizationProjectsList.as_view(), name='organization_projects_list'),
url(r'^(?P<pk>[0-9]+)/job_templates/$', OrganizationJobTemplatesList.as_view(), name='organization_job_templates_list'), re_path(r'^(?P<pk>[0-9]+)/job_templates/$', OrganizationJobTemplatesList.as_view(), name='organization_job_templates_list'),
url(r'^(?P<pk>[0-9]+)/workflow_job_templates/$', OrganizationWorkflowJobTemplatesList.as_view(), name='organization_workflow_job_templates_list'), re_path(r'^(?P<pk>[0-9]+)/workflow_job_templates/$', OrganizationWorkflowJobTemplatesList.as_view(), name='organization_workflow_job_templates_list'),
url(r'^(?P<pk>[0-9]+)/teams/$', OrganizationTeamsList.as_view(), name='organization_teams_list'), re_path(r'^(?P<pk>[0-9]+)/teams/$', OrganizationTeamsList.as_view(), name='organization_teams_list'),
url(r'^(?P<pk>[0-9]+)/credentials/$', OrganizationCredentialList.as_view(), name='organization_credential_list'), re_path(r'^(?P<pk>[0-9]+)/credentials/$', OrganizationCredentialList.as_view(), name='organization_credential_list'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', OrganizationActivityStreamList.as_view(), name='organization_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', OrganizationActivityStreamList.as_view(), name='organization_activity_stream_list'),
url(r'^(?P<pk>[0-9]+)/notification_templates/$', OrganizationNotificationTemplatesList.as_view(), name='organization_notification_templates_list'), re_path(r'^(?P<pk>[0-9]+)/notification_templates/$', OrganizationNotificationTemplatesList.as_view(), name='organization_notification_templates_list'),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_started/$', r'^(?P<pk>[0-9]+)/notification_templates_started/$',
OrganizationNotificationTemplatesStartedList.as_view(), OrganizationNotificationTemplatesStartedList.as_view(),
name='organization_notification_templates_started_list', name='organization_notification_templates_started_list',
), ),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_error/$', r'^(?P<pk>[0-9]+)/notification_templates_error/$',
OrganizationNotificationTemplatesErrorList.as_view(), OrganizationNotificationTemplatesErrorList.as_view(),
name='organization_notification_templates_error_list', name='organization_notification_templates_error_list',
), ),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_success/$', r'^(?P<pk>[0-9]+)/notification_templates_success/$',
OrganizationNotificationTemplatesSuccessList.as_view(), OrganizationNotificationTemplatesSuccessList.as_view(),
name='organization_notification_templates_success_list', name='organization_notification_templates_success_list',
), ),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_approvals/$', r'^(?P<pk>[0-9]+)/notification_templates_approvals/$',
OrganizationNotificationTemplatesApprovalList.as_view(), OrganizationNotificationTemplatesApprovalList.as_view(),
name='organization_notification_templates_approvals_list', name='organization_notification_templates_approvals_list',
), ),
url(r'^(?P<pk>[0-9]+)/instance_groups/$', OrganizationInstanceGroupsList.as_view(), name='organization_instance_groups_list'), re_path(r'^(?P<pk>[0-9]+)/instance_groups/$', OrganizationInstanceGroupsList.as_view(), name='organization_instance_groups_list'),
url(r'^(?P<pk>[0-9]+)/galaxy_credentials/$', OrganizationGalaxyCredentialsList.as_view(), name='organization_galaxy_credentials_list'), re_path(r'^(?P<pk>[0-9]+)/galaxy_credentials/$', OrganizationGalaxyCredentialsList.as_view(), name='organization_galaxy_credentials_list'),
url(r'^(?P<pk>[0-9]+)/object_roles/$', OrganizationObjectRolesList.as_view(), name='organization_object_roles_list'), re_path(r'^(?P<pk>[0-9]+)/object_roles/$', OrganizationObjectRolesList.as_view(), name='organization_object_roles_list'),
url(r'^(?P<pk>[0-9]+)/access_list/$', OrganizationAccessList.as_view(), name='organization_access_list'), re_path(r'^(?P<pk>[0-9]+)/access_list/$', OrganizationAccessList.as_view(), name='organization_access_list'),
url(r'^(?P<pk>[0-9]+)/applications/$', OrganizationApplicationList.as_view(), name='organization_applications_list'), re_path(r'^(?P<pk>[0-9]+)/applications/$', OrganizationApplicationList.as_view(), name='organization_applications_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
ProjectList, ProjectList,
@@ -24,30 +24,32 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', ProjectList.as_view(), name='project_list'), re_path(r'^$', ProjectList.as_view(), name='project_list'),
url(r'^(?P<pk>[0-9]+)/$', ProjectDetail.as_view(), name='project_detail'), re_path(r'^(?P<pk>[0-9]+)/$', ProjectDetail.as_view(), name='project_detail'),
url(r'^(?P<pk>[0-9]+)/playbooks/$', ProjectPlaybooks.as_view(), name='project_playbooks'), re_path(r'^(?P<pk>[0-9]+)/playbooks/$', ProjectPlaybooks.as_view(), name='project_playbooks'),
url(r'^(?P<pk>[0-9]+)/inventories/$', ProjectInventories.as_view(), name='project_inventories'), re_path(r'^(?P<pk>[0-9]+)/inventories/$', ProjectInventories.as_view(), name='project_inventories'),
url(r'^(?P<pk>[0-9]+)/scm_inventory_sources/$', ProjectScmInventorySources.as_view(), name='project_scm_inventory_sources'), re_path(r'^(?P<pk>[0-9]+)/scm_inventory_sources/$', ProjectScmInventorySources.as_view(), name='project_scm_inventory_sources'),
url(r'^(?P<pk>[0-9]+)/teams/$', ProjectTeamsList.as_view(), name='project_teams_list'), re_path(r'^(?P<pk>[0-9]+)/teams/$', ProjectTeamsList.as_view(), name='project_teams_list'),
url(r'^(?P<pk>[0-9]+)/update/$', ProjectUpdateView.as_view(), name='project_update_view'), re_path(r'^(?P<pk>[0-9]+)/update/$', ProjectUpdateView.as_view(), name='project_update_view'),
url(r'^(?P<pk>[0-9]+)/project_updates/$', ProjectUpdatesList.as_view(), name='project_updates_list'), re_path(r'^(?P<pk>[0-9]+)/project_updates/$', ProjectUpdatesList.as_view(), name='project_updates_list'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', ProjectActivityStreamList.as_view(), name='project_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', ProjectActivityStreamList.as_view(), name='project_activity_stream_list'),
url(r'^(?P<pk>[0-9]+)/schedules/$', ProjectSchedulesList.as_view(), name='project_schedules_list'), re_path(r'^(?P<pk>[0-9]+)/schedules/$', ProjectSchedulesList.as_view(), name='project_schedules_list'),
url(r'^(?P<pk>[0-9]+)/notification_templates_error/$', ProjectNotificationTemplatesErrorList.as_view(), name='project_notification_templates_error_list'), re_path(
url( r'^(?P<pk>[0-9]+)/notification_templates_error/$', ProjectNotificationTemplatesErrorList.as_view(), name='project_notification_templates_error_list'
),
re_path(
r'^(?P<pk>[0-9]+)/notification_templates_success/$', r'^(?P<pk>[0-9]+)/notification_templates_success/$',
ProjectNotificationTemplatesSuccessList.as_view(), ProjectNotificationTemplatesSuccessList.as_view(),
name='project_notification_templates_success_list', name='project_notification_templates_success_list',
), ),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_started/$', r'^(?P<pk>[0-9]+)/notification_templates_started/$',
ProjectNotificationTemplatesStartedList.as_view(), ProjectNotificationTemplatesStartedList.as_view(),
name='project_notification_templates_started_list', name='project_notification_templates_started_list',
), ),
url(r'^(?P<pk>[0-9]+)/object_roles/$', ProjectObjectRolesList.as_view(), name='project_object_roles_list'), re_path(r'^(?P<pk>[0-9]+)/object_roles/$', ProjectObjectRolesList.as_view(), name='project_object_roles_list'),
url(r'^(?P<pk>[0-9]+)/access_list/$', ProjectAccessList.as_view(), name='project_access_list'), re_path(r'^(?P<pk>[0-9]+)/access_list/$', ProjectAccessList.as_view(), name='project_access_list'),
url(r'^(?P<pk>[0-9]+)/copy/$', ProjectCopy.as_view(), name='project_copy'), re_path(r'^(?P<pk>[0-9]+)/copy/$', ProjectCopy.as_view(), name='project_copy'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
ProjectUpdateList, ProjectUpdateList,
@@ -15,13 +15,13 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', ProjectUpdateList.as_view(), name='project_update_list'), re_path(r'^$', ProjectUpdateList.as_view(), name='project_update_list'),
url(r'^(?P<pk>[0-9]+)/$', ProjectUpdateDetail.as_view(), name='project_update_detail'), re_path(r'^(?P<pk>[0-9]+)/$', ProjectUpdateDetail.as_view(), name='project_update_detail'),
url(r'^(?P<pk>[0-9]+)/cancel/$', ProjectUpdateCancel.as_view(), name='project_update_cancel'), re_path(r'^(?P<pk>[0-9]+)/cancel/$', ProjectUpdateCancel.as_view(), name='project_update_cancel'),
url(r'^(?P<pk>[0-9]+)/stdout/$', ProjectUpdateStdout.as_view(), name='project_update_stdout'), re_path(r'^(?P<pk>[0-9]+)/stdout/$', ProjectUpdateStdout.as_view(), name='project_update_stdout'),
url(r'^(?P<pk>[0-9]+)/scm_inventory_updates/$', ProjectUpdateScmInventoryUpdates.as_view(), name='project_update_scm_inventory_updates'), re_path(r'^(?P<pk>[0-9]+)/scm_inventory_updates/$', ProjectUpdateScmInventoryUpdates.as_view(), name='project_update_scm_inventory_updates'),
url(r'^(?P<pk>[0-9]+)/notifications/$', ProjectUpdateNotificationsList.as_view(), name='project_update_notifications_list'), re_path(r'^(?P<pk>[0-9]+)/notifications/$', ProjectUpdateNotificationsList.as_view(), name='project_update_notifications_list'),
url(r'^(?P<pk>[0-9]+)/events/$', ProjectUpdateEventsList.as_view(), name='project_update_events_list'), re_path(r'^(?P<pk>[0-9]+)/events/$', ProjectUpdateEventsList.as_view(), name='project_update_events_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,18 +1,18 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import RoleList, RoleDetail, RoleUsersList, RoleTeamsList, RoleParentsList, RoleChildrenList from awx.api.views import RoleList, RoleDetail, RoleUsersList, RoleTeamsList, RoleParentsList, RoleChildrenList
urls = [ urls = [
url(r'^$', RoleList.as_view(), name='role_list'), re_path(r'^$', RoleList.as_view(), name='role_list'),
url(r'^(?P<pk>[0-9]+)/$', RoleDetail.as_view(), name='role_detail'), re_path(r'^(?P<pk>[0-9]+)/$', RoleDetail.as_view(), name='role_detail'),
url(r'^(?P<pk>[0-9]+)/users/$', RoleUsersList.as_view(), name='role_users_list'), re_path(r'^(?P<pk>[0-9]+)/users/$', RoleUsersList.as_view(), name='role_users_list'),
url(r'^(?P<pk>[0-9]+)/teams/$', RoleTeamsList.as_view(), name='role_teams_list'), re_path(r'^(?P<pk>[0-9]+)/teams/$', RoleTeamsList.as_view(), name='role_teams_list'),
url(r'^(?P<pk>[0-9]+)/parents/$', RoleParentsList.as_view(), name='role_parents_list'), re_path(r'^(?P<pk>[0-9]+)/parents/$', RoleParentsList.as_view(), name='role_parents_list'),
url(r'^(?P<pk>[0-9]+)/children/$', RoleChildrenList.as_view(), name='role_children_list'), re_path(r'^(?P<pk>[0-9]+)/children/$', RoleChildrenList.as_view(), name='role_children_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,16 +1,16 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ScheduleList, ScheduleDetail, ScheduleUnifiedJobsList, ScheduleCredentialsList from awx.api.views import ScheduleList, ScheduleDetail, ScheduleUnifiedJobsList, ScheduleCredentialsList
urls = [ urls = [
url(r'^$', ScheduleList.as_view(), name='schedule_list'), re_path(r'^$', ScheduleList.as_view(), name='schedule_list'),
url(r'^(?P<pk>[0-9]+)/$', ScheduleDetail.as_view(), name='schedule_detail'), re_path(r'^(?P<pk>[0-9]+)/$', ScheduleDetail.as_view(), name='schedule_detail'),
url(r'^(?P<pk>[0-9]+)/jobs/$', ScheduleUnifiedJobsList.as_view(), name='schedule_unified_jobs_list'), re_path(r'^(?P<pk>[0-9]+)/jobs/$', ScheduleUnifiedJobsList.as_view(), name='schedule_unified_jobs_list'),
url(r'^(?P<pk>[0-9]+)/credentials/$', ScheduleCredentialsList.as_view(), name='schedule_credentials_list'), re_path(r'^(?P<pk>[0-9]+)/credentials/$', ScheduleCredentialsList.as_view(), name='schedule_credentials_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,17 +1,17 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import SystemJobList, SystemJobDetail, SystemJobCancel, SystemJobNotificationsList, SystemJobEventsList from awx.api.views import SystemJobList, SystemJobDetail, SystemJobCancel, SystemJobNotificationsList, SystemJobEventsList
urls = [ urls = [
url(r'^$', SystemJobList.as_view(), name='system_job_list'), re_path(r'^$', SystemJobList.as_view(), name='system_job_list'),
url(r'^(?P<pk>[0-9]+)/$', SystemJobDetail.as_view(), name='system_job_detail'), re_path(r'^(?P<pk>[0-9]+)/$', SystemJobDetail.as_view(), name='system_job_detail'),
url(r'^(?P<pk>[0-9]+)/cancel/$', SystemJobCancel.as_view(), name='system_job_cancel'), re_path(r'^(?P<pk>[0-9]+)/cancel/$', SystemJobCancel.as_view(), name='system_job_cancel'),
url(r'^(?P<pk>[0-9]+)/notifications/$', SystemJobNotificationsList.as_view(), name='system_job_notifications_list'), re_path(r'^(?P<pk>[0-9]+)/notifications/$', SystemJobNotificationsList.as_view(), name='system_job_notifications_list'),
url(r'^(?P<pk>[0-9]+)/events/$', SystemJobEventsList.as_view(), name='system_job_events_list'), re_path(r'^(?P<pk>[0-9]+)/events/$', SystemJobEventsList.as_view(), name='system_job_events_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
SystemJobTemplateList, SystemJobTemplateList,
@@ -16,22 +16,22 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', SystemJobTemplateList.as_view(), name='system_job_template_list'), re_path(r'^$', SystemJobTemplateList.as_view(), name='system_job_template_list'),
url(r'^(?P<pk>[0-9]+)/$', SystemJobTemplateDetail.as_view(), name='system_job_template_detail'), re_path(r'^(?P<pk>[0-9]+)/$', SystemJobTemplateDetail.as_view(), name='system_job_template_detail'),
url(r'^(?P<pk>[0-9]+)/launch/$', SystemJobTemplateLaunch.as_view(), name='system_job_template_launch'), re_path(r'^(?P<pk>[0-9]+)/launch/$', SystemJobTemplateLaunch.as_view(), name='system_job_template_launch'),
url(r'^(?P<pk>[0-9]+)/jobs/$', SystemJobTemplateJobsList.as_view(), name='system_job_template_jobs_list'), re_path(r'^(?P<pk>[0-9]+)/jobs/$', SystemJobTemplateJobsList.as_view(), name='system_job_template_jobs_list'),
url(r'^(?P<pk>[0-9]+)/schedules/$', SystemJobTemplateSchedulesList.as_view(), name='system_job_template_schedules_list'), re_path(r'^(?P<pk>[0-9]+)/schedules/$', SystemJobTemplateSchedulesList.as_view(), name='system_job_template_schedules_list'),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_started/$', r'^(?P<pk>[0-9]+)/notification_templates_started/$',
SystemJobTemplateNotificationTemplatesStartedList.as_view(), SystemJobTemplateNotificationTemplatesStartedList.as_view(),
name='system_job_template_notification_templates_started_list', name='system_job_template_notification_templates_started_list',
), ),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_error/$', r'^(?P<pk>[0-9]+)/notification_templates_error/$',
SystemJobTemplateNotificationTemplatesErrorList.as_view(), SystemJobTemplateNotificationTemplatesErrorList.as_view(),
name='system_job_template_notification_templates_error_list', name='system_job_template_notification_templates_error_list',
), ),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_success/$', r'^(?P<pk>[0-9]+)/notification_templates_success/$',
SystemJobTemplateNotificationTemplatesSuccessList.as_view(), SystemJobTemplateNotificationTemplatesSuccessList.as_view(),
name='system_job_template_notification_templates_success_list', name='system_job_template_notification_templates_success_list',

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
TeamList, TeamList,
@@ -17,15 +17,15 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', TeamList.as_view(), name='team_list'), re_path(r'^$', TeamList.as_view(), name='team_list'),
url(r'^(?P<pk>[0-9]+)/$', TeamDetail.as_view(), name='team_detail'), re_path(r'^(?P<pk>[0-9]+)/$', TeamDetail.as_view(), name='team_detail'),
url(r'^(?P<pk>[0-9]+)/projects/$', TeamProjectsList.as_view(), name='team_projects_list'), re_path(r'^(?P<pk>[0-9]+)/projects/$', TeamProjectsList.as_view(), name='team_projects_list'),
url(r'^(?P<pk>[0-9]+)/users/$', TeamUsersList.as_view(), name='team_users_list'), re_path(r'^(?P<pk>[0-9]+)/users/$', TeamUsersList.as_view(), name='team_users_list'),
url(r'^(?P<pk>[0-9]+)/credentials/$', TeamCredentialsList.as_view(), name='team_credentials_list'), re_path(r'^(?P<pk>[0-9]+)/credentials/$', TeamCredentialsList.as_view(), name='team_credentials_list'),
url(r'^(?P<pk>[0-9]+)/roles/$', TeamRolesList.as_view(), name='team_roles_list'), re_path(r'^(?P<pk>[0-9]+)/roles/$', TeamRolesList.as_view(), name='team_roles_list'),
url(r'^(?P<pk>[0-9]+)/object_roles/$', TeamObjectRolesList.as_view(), name='team_object_roles_list'), re_path(r'^(?P<pk>[0-9]+)/object_roles/$', TeamObjectRolesList.as_view(), name='team_object_roles_list'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', TeamActivityStreamList.as_view(), name='team_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', TeamActivityStreamList.as_view(), name='team_activity_stream_list'),
url(r'^(?P<pk>[0-9]+)/access_list/$', TeamAccessList.as_view(), name='team_access_list'), re_path(r'^(?P<pk>[0-9]+)/access_list/$', TeamAccessList.as_view(), name='team_access_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -3,7 +3,7 @@
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
from django.conf import settings from django.conf import settings
from django.conf.urls import include, url from django.urls import include, re_path
from awx.api.generics import LoggedLoginView, LoggedLogoutView from awx.api.generics import LoggedLoginView, LoggedLogoutView
from awx.api.views import ( from awx.api.views import (
@@ -74,78 +74,78 @@ from .workflow_approval import urls as workflow_approval_urls
v2_urls = [ v2_urls = [
url(r'^$', ApiV2RootView.as_view(), name='api_v2_root_view'), re_path(r'^$', ApiV2RootView.as_view(), name='api_v2_root_view'),
url(r'^credential_types/', include(credential_type_urls)), re_path(r'^credential_types/', include(credential_type_urls)),
url(r'^credential_input_sources/', include(credential_input_source_urls)), re_path(r'^credential_input_sources/', include(credential_input_source_urls)),
url(r'^hosts/(?P<pk>[0-9]+)/ansible_facts/$', HostAnsibleFactsDetail.as_view(), name='host_ansible_facts_detail'), re_path(r'^hosts/(?P<pk>[0-9]+)/ansible_facts/$', HostAnsibleFactsDetail.as_view(), name='host_ansible_facts_detail'),
url(r'^jobs/(?P<pk>[0-9]+)/credentials/$', JobCredentialsList.as_view(), name='job_credentials_list'), re_path(r'^jobs/(?P<pk>[0-9]+)/credentials/$', JobCredentialsList.as_view(), name='job_credentials_list'),
url(r'^job_templates/(?P<pk>[0-9]+)/credentials/$', JobTemplateCredentialsList.as_view(), name='job_template_credentials_list'), re_path(r'^job_templates/(?P<pk>[0-9]+)/credentials/$', JobTemplateCredentialsList.as_view(), name='job_template_credentials_list'),
url(r'^schedules/preview/$', SchedulePreview.as_view(), name='schedule_rrule'), re_path(r'^schedules/preview/$', SchedulePreview.as_view(), name='schedule_rrule'),
url(r'^schedules/zoneinfo/$', ScheduleZoneInfo.as_view(), name='schedule_zoneinfo'), re_path(r'^schedules/zoneinfo/$', ScheduleZoneInfo.as_view(), name='schedule_zoneinfo'),
url(r'^applications/$', OAuth2ApplicationList.as_view(), name='o_auth2_application_list'), re_path(r'^applications/$', OAuth2ApplicationList.as_view(), name='o_auth2_application_list'),
url(r'^applications/(?P<pk>[0-9]+)/$', OAuth2ApplicationDetail.as_view(), name='o_auth2_application_detail'), re_path(r'^applications/(?P<pk>[0-9]+)/$', OAuth2ApplicationDetail.as_view(), name='o_auth2_application_detail'),
url(r'^applications/(?P<pk>[0-9]+)/tokens/$', ApplicationOAuth2TokenList.as_view(), name='application_o_auth2_token_list'), re_path(r'^applications/(?P<pk>[0-9]+)/tokens/$', ApplicationOAuth2TokenList.as_view(), name='application_o_auth2_token_list'),
url(r'^tokens/$', OAuth2TokenList.as_view(), name='o_auth2_token_list'), re_path(r'^tokens/$', OAuth2TokenList.as_view(), name='o_auth2_token_list'),
url(r'^', include(oauth2_urls)), re_path(r'^', include(oauth2_urls)),
url(r'^metrics/$', MetricsView.as_view(), name='metrics_view'), re_path(r'^metrics/$', MetricsView.as_view(), name='metrics_view'),
url(r'^ping/$', ApiV2PingView.as_view(), name='api_v2_ping_view'), re_path(r'^ping/$', ApiV2PingView.as_view(), name='api_v2_ping_view'),
url(r'^config/$', ApiV2ConfigView.as_view(), name='api_v2_config_view'), re_path(r'^config/$', ApiV2ConfigView.as_view(), name='api_v2_config_view'),
url(r'^config/subscriptions/$', ApiV2SubscriptionView.as_view(), name='api_v2_subscription_view'), re_path(r'^config/subscriptions/$', ApiV2SubscriptionView.as_view(), name='api_v2_subscription_view'),
url(r'^config/attach/$', ApiV2AttachView.as_view(), name='api_v2_attach_view'), re_path(r'^config/attach/$', ApiV2AttachView.as_view(), name='api_v2_attach_view'),
url(r'^auth/$', AuthView.as_view()), re_path(r'^auth/$', AuthView.as_view()),
url(r'^me/$', UserMeList.as_view(), name='user_me_list'), re_path(r'^me/$', UserMeList.as_view(), name='user_me_list'),
url(r'^dashboard/$', DashboardView.as_view(), name='dashboard_view'), re_path(r'^dashboard/$', DashboardView.as_view(), name='dashboard_view'),
url(r'^dashboard/graphs/jobs/$', DashboardJobsGraphView.as_view(), name='dashboard_jobs_graph_view'), re_path(r'^dashboard/graphs/jobs/$', DashboardJobsGraphView.as_view(), name='dashboard_jobs_graph_view'),
url(r'^mesh_visualizer/', MeshVisualizer.as_view(), name='mesh_visualizer_view'), re_path(r'^mesh_visualizer/', MeshVisualizer.as_view(), name='mesh_visualizer_view'),
url(r'^settings/', include('awx.conf.urls')), re_path(r'^settings/', include('awx.conf.urls')),
url(r'^instances/', include(instance_urls)), re_path(r'^instances/', include(instance_urls)),
url(r'^instance_groups/', include(instance_group_urls)), re_path(r'^instance_groups/', include(instance_group_urls)),
url(r'^schedules/', include(schedule_urls)), re_path(r'^schedules/', include(schedule_urls)),
url(r'^organizations/', include(organization_urls)), re_path(r'^organizations/', include(organization_urls)),
url(r'^users/', include(user_urls)), re_path(r'^users/', include(user_urls)),
url(r'^execution_environments/', include(execution_environment_urls)), re_path(r'^execution_environments/', include(execution_environment_urls)),
url(r'^projects/', include(project_urls)), re_path(r'^projects/', include(project_urls)),
url(r'^project_updates/', include(project_update_urls)), re_path(r'^project_updates/', include(project_update_urls)),
url(r'^teams/', include(team_urls)), re_path(r'^teams/', include(team_urls)),
url(r'^inventories/', include(inventory_urls)), re_path(r'^inventories/', include(inventory_urls)),
url(r'^hosts/', include(host_urls)), re_path(r'^hosts/', include(host_urls)),
url(r'^groups/', include(group_urls)), re_path(r'^groups/', include(group_urls)),
url(r'^inventory_sources/', include(inventory_source_urls)), re_path(r'^inventory_sources/', include(inventory_source_urls)),
url(r'^inventory_updates/', include(inventory_update_urls)), re_path(r'^inventory_updates/', include(inventory_update_urls)),
url(r'^credentials/', include(credential_urls)), re_path(r'^credentials/', include(credential_urls)),
url(r'^roles/', include(role_urls)), re_path(r'^roles/', include(role_urls)),
url(r'^job_templates/', include(job_template_urls)), re_path(r'^job_templates/', include(job_template_urls)),
url(r'^jobs/', include(job_urls)), re_path(r'^jobs/', include(job_urls)),
url(r'^job_host_summaries/', include(job_host_summary_urls)), re_path(r'^job_host_summaries/', include(job_host_summary_urls)),
url(r'^job_events/', include(job_event_urls)), re_path(r'^job_events/', include(job_event_urls)),
url(r'^ad_hoc_commands/', include(ad_hoc_command_urls)), re_path(r'^ad_hoc_commands/', include(ad_hoc_command_urls)),
url(r'^ad_hoc_command_events/', include(ad_hoc_command_event_urls)), re_path(r'^ad_hoc_command_events/', include(ad_hoc_command_event_urls)),
url(r'^system_job_templates/', include(system_job_template_urls)), re_path(r'^system_job_templates/', include(system_job_template_urls)),
url(r'^system_jobs/', include(system_job_urls)), re_path(r'^system_jobs/', include(system_job_urls)),
url(r'^notification_templates/', include(notification_template_urls)), re_path(r'^notification_templates/', include(notification_template_urls)),
url(r'^notifications/', include(notification_urls)), re_path(r'^notifications/', include(notification_urls)),
url(r'^workflow_job_templates/', include(workflow_job_template_urls)), re_path(r'^workflow_job_templates/', include(workflow_job_template_urls)),
url(r'^workflow_jobs/', include(workflow_job_urls)), re_path(r'^workflow_jobs/', include(workflow_job_urls)),
url(r'^labels/', include(label_urls)), re_path(r'^labels/', include(label_urls)),
url(r'^workflow_job_template_nodes/', include(workflow_job_template_node_urls)), re_path(r'^workflow_job_template_nodes/', include(workflow_job_template_node_urls)),
url(r'^workflow_job_nodes/', include(workflow_job_node_urls)), re_path(r'^workflow_job_nodes/', include(workflow_job_node_urls)),
url(r'^unified_job_templates/$', UnifiedJobTemplateList.as_view(), name='unified_job_template_list'), re_path(r'^unified_job_templates/$', UnifiedJobTemplateList.as_view(), name='unified_job_template_list'),
url(r'^unified_jobs/$', UnifiedJobList.as_view(), name='unified_job_list'), re_path(r'^unified_jobs/$', UnifiedJobList.as_view(), name='unified_job_list'),
url(r'^activity_stream/', include(activity_stream_urls)), re_path(r'^activity_stream/', include(activity_stream_urls)),
url(r'^workflow_approval_templates/', include(workflow_approval_template_urls)), re_path(r'^workflow_approval_templates/', include(workflow_approval_template_urls)),
url(r'^workflow_approvals/', include(workflow_approval_urls)), re_path(r'^workflow_approvals/', include(workflow_approval_urls)),
] ]
app_name = 'api' app_name = 'api'
urlpatterns = [ urlpatterns = [
url(r'^$', ApiRootView.as_view(), name='api_root_view'), re_path(r'^$', ApiRootView.as_view(), name='api_root_view'),
url(r'^(?P<version>(v2))/', include(v2_urls)), re_path(r'^(?P<version>(v2))/', include(v2_urls)),
url(r'^login/$', LoggedLoginView.as_view(template_name='rest_framework/login.html', extra_context={'inside_login_context': True}), name='login'), re_path(r'^login/$', LoggedLoginView.as_view(template_name='rest_framework/login.html', extra_context={'inside_login_context': True}), name='login'),
url(r'^logout/$', LoggedLogoutView.as_view(next_page='/api/', redirect_field_name='next'), name='logout'), re_path(r'^logout/$', LoggedLogoutView.as_view(next_page='/api/', redirect_field_name='next'), name='logout'),
url(r'^o/', include(oauth2_root_urls)), re_path(r'^o/', include(oauth2_root_urls)),
] ]
if settings.SETTINGS_MODULE == 'awx.settings.development': if settings.SETTINGS_MODULE == 'awx.settings.development':
from awx.api.swagger import SwaggerSchemaView from awx.api.swagger import SwaggerSchemaView
urlpatterns += [url(r'^swagger/$', SwaggerSchemaView.as_view(), name='swagger_view')] urlpatterns += [re_path(r'^swagger/$', SwaggerSchemaView.as_view(), name='swagger_view')]

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
UserList, UserList,
@@ -21,20 +21,20 @@ from awx.api.views import (
) )
urls = [ urls = [
url(r'^$', UserList.as_view(), name='user_list'), re_path(r'^$', UserList.as_view(), name='user_list'),
url(r'^(?P<pk>[0-9]+)/$', UserDetail.as_view(), name='user_detail'), re_path(r'^(?P<pk>[0-9]+)/$', UserDetail.as_view(), name='user_detail'),
url(r'^(?P<pk>[0-9]+)/teams/$', UserTeamsList.as_view(), name='user_teams_list'), re_path(r'^(?P<pk>[0-9]+)/teams/$', UserTeamsList.as_view(), name='user_teams_list'),
url(r'^(?P<pk>[0-9]+)/organizations/$', UserOrganizationsList.as_view(), name='user_organizations_list'), re_path(r'^(?P<pk>[0-9]+)/organizations/$', UserOrganizationsList.as_view(), name='user_organizations_list'),
url(r'^(?P<pk>[0-9]+)/admin_of_organizations/$', UserAdminOfOrganizationsList.as_view(), name='user_admin_of_organizations_list'), re_path(r'^(?P<pk>[0-9]+)/admin_of_organizations/$', UserAdminOfOrganizationsList.as_view(), name='user_admin_of_organizations_list'),
url(r'^(?P<pk>[0-9]+)/projects/$', UserProjectsList.as_view(), name='user_projects_list'), re_path(r'^(?P<pk>[0-9]+)/projects/$', UserProjectsList.as_view(), name='user_projects_list'),
url(r'^(?P<pk>[0-9]+)/credentials/$', UserCredentialsList.as_view(), name='user_credentials_list'), re_path(r'^(?P<pk>[0-9]+)/credentials/$', UserCredentialsList.as_view(), name='user_credentials_list'),
url(r'^(?P<pk>[0-9]+)/roles/$', UserRolesList.as_view(), name='user_roles_list'), re_path(r'^(?P<pk>[0-9]+)/roles/$', UserRolesList.as_view(), name='user_roles_list'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', UserActivityStreamList.as_view(), name='user_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', UserActivityStreamList.as_view(), name='user_activity_stream_list'),
url(r'^(?P<pk>[0-9]+)/access_list/$', UserAccessList.as_view(), name='user_access_list'), re_path(r'^(?P<pk>[0-9]+)/access_list/$', UserAccessList.as_view(), name='user_access_list'),
url(r'^(?P<pk>[0-9]+)/applications/$', OAuth2ApplicationList.as_view(), name='o_auth2_application_list'), re_path(r'^(?P<pk>[0-9]+)/applications/$', OAuth2ApplicationList.as_view(), name='o_auth2_application_list'),
url(r'^(?P<pk>[0-9]+)/tokens/$', OAuth2UserTokenList.as_view(), name='o_auth2_token_list'), re_path(r'^(?P<pk>[0-9]+)/tokens/$', OAuth2UserTokenList.as_view(), name='o_auth2_token_list'),
url(r'^(?P<pk>[0-9]+)/authorized_tokens/$', UserAuthorizedTokenList.as_view(), name='user_authorized_token_list'), re_path(r'^(?P<pk>[0-9]+)/authorized_tokens/$', UserAuthorizedTokenList.as_view(), name='user_authorized_token_list'),
url(r'^(?P<pk>[0-9]+)/personal_tokens/$', UserPersonalTokenList.as_view(), name='user_personal_token_list'), re_path(r'^(?P<pk>[0-9]+)/personal_tokens/$', UserPersonalTokenList.as_view(), name='user_personal_token_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,10 +1,10 @@
from django.conf.urls import url from django.urls import re_path
from awx.api.views import WebhookKeyView, GithubWebhookReceiver, GitlabWebhookReceiver from awx.api.views import WebhookKeyView, GithubWebhookReceiver, GitlabWebhookReceiver
urlpatterns = [ urlpatterns = [
url(r'^webhook_key/$', WebhookKeyView.as_view(), name='webhook_key'), re_path(r'^webhook_key/$', WebhookKeyView.as_view(), name='webhook_key'),
url(r'^github/$', GithubWebhookReceiver.as_view(), name='webhook_receiver_github'), re_path(r'^github/$', GithubWebhookReceiver.as_view(), name='webhook_receiver_github'),
url(r'^gitlab/$', GitlabWebhookReceiver.as_view(), name='webhook_receiver_gitlab'), re_path(r'^gitlab/$', GitlabWebhookReceiver.as_view(), name='webhook_receiver_gitlab'),
] ]

View File

@@ -1,16 +1,16 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import WorkflowApprovalList, WorkflowApprovalDetail, WorkflowApprovalApprove, WorkflowApprovalDeny from awx.api.views import WorkflowApprovalList, WorkflowApprovalDetail, WorkflowApprovalApprove, WorkflowApprovalDeny
urls = [ urls = [
url(r'^$', WorkflowApprovalList.as_view(), name='workflow_approval_list'), re_path(r'^$', WorkflowApprovalList.as_view(), name='workflow_approval_list'),
url(r'^(?P<pk>[0-9]+)/$', WorkflowApprovalDetail.as_view(), name='workflow_approval_detail'), re_path(r'^(?P<pk>[0-9]+)/$', WorkflowApprovalDetail.as_view(), name='workflow_approval_detail'),
url(r'^(?P<pk>[0-9]+)/approve/$', WorkflowApprovalApprove.as_view(), name='workflow_approval_approve'), re_path(r'^(?P<pk>[0-9]+)/approve/$', WorkflowApprovalApprove.as_view(), name='workflow_approval_approve'),
url(r'^(?P<pk>[0-9]+)/deny/$', WorkflowApprovalDeny.as_view(), name='workflow_approval_deny'), re_path(r'^(?P<pk>[0-9]+)/deny/$', WorkflowApprovalDeny.as_view(), name='workflow_approval_deny'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,14 +1,14 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import WorkflowApprovalTemplateDetail, WorkflowApprovalTemplateJobsList from awx.api.views import WorkflowApprovalTemplateDetail, WorkflowApprovalTemplateJobsList
urls = [ urls = [
url(r'^(?P<pk>[0-9]+)/$', WorkflowApprovalTemplateDetail.as_view(), name='workflow_approval_template_detail'), re_path(r'^(?P<pk>[0-9]+)/$', WorkflowApprovalTemplateDetail.as_view(), name='workflow_approval_template_detail'),
url(r'^(?P<pk>[0-9]+)/approvals/$', WorkflowApprovalTemplateJobsList.as_view(), name='workflow_approval_template_jobs_list'), re_path(r'^(?P<pk>[0-9]+)/approvals/$', WorkflowApprovalTemplateJobsList.as_view(), name='workflow_approval_template_jobs_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
WorkflowJobList, WorkflowJobList,
@@ -16,14 +16,14 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', WorkflowJobList.as_view(), name='workflow_job_list'), re_path(r'^$', WorkflowJobList.as_view(), name='workflow_job_list'),
url(r'^(?P<pk>[0-9]+)/$', WorkflowJobDetail.as_view(), name='workflow_job_detail'), re_path(r'^(?P<pk>[0-9]+)/$', WorkflowJobDetail.as_view(), name='workflow_job_detail'),
url(r'^(?P<pk>[0-9]+)/workflow_nodes/$', WorkflowJobWorkflowNodesList.as_view(), name='workflow_job_workflow_nodes_list'), re_path(r'^(?P<pk>[0-9]+)/workflow_nodes/$', WorkflowJobWorkflowNodesList.as_view(), name='workflow_job_workflow_nodes_list'),
url(r'^(?P<pk>[0-9]+)/labels/$', WorkflowJobLabelList.as_view(), name='workflow_job_label_list'), re_path(r'^(?P<pk>[0-9]+)/labels/$', WorkflowJobLabelList.as_view(), name='workflow_job_label_list'),
url(r'^(?P<pk>[0-9]+)/cancel/$', WorkflowJobCancel.as_view(), name='workflow_job_cancel'), re_path(r'^(?P<pk>[0-9]+)/cancel/$', WorkflowJobCancel.as_view(), name='workflow_job_cancel'),
url(r'^(?P<pk>[0-9]+)/relaunch/$', WorkflowJobRelaunch.as_view(), name='workflow_job_relaunch'), re_path(r'^(?P<pk>[0-9]+)/relaunch/$', WorkflowJobRelaunch.as_view(), name='workflow_job_relaunch'),
url(r'^(?P<pk>[0-9]+)/notifications/$', WorkflowJobNotificationsList.as_view(), name='workflow_job_notifications_list'), re_path(r'^(?P<pk>[0-9]+)/notifications/$', WorkflowJobNotificationsList.as_view(), name='workflow_job_notifications_list'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', WorkflowJobActivityStreamList.as_view(), name='workflow_job_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', WorkflowJobActivityStreamList.as_view(), name='workflow_job_activity_stream_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
WorkflowJobNodeList, WorkflowJobNodeList,
@@ -14,12 +14,12 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', WorkflowJobNodeList.as_view(), name='workflow_job_node_list'), re_path(r'^$', WorkflowJobNodeList.as_view(), name='workflow_job_node_list'),
url(r'^(?P<pk>[0-9]+)/$', WorkflowJobNodeDetail.as_view(), name='workflow_job_node_detail'), re_path(r'^(?P<pk>[0-9]+)/$', WorkflowJobNodeDetail.as_view(), name='workflow_job_node_detail'),
url(r'^(?P<pk>[0-9]+)/success_nodes/$', WorkflowJobNodeSuccessNodesList.as_view(), name='workflow_job_node_success_nodes_list'), re_path(r'^(?P<pk>[0-9]+)/success_nodes/$', WorkflowJobNodeSuccessNodesList.as_view(), name='workflow_job_node_success_nodes_list'),
url(r'^(?P<pk>[0-9]+)/failure_nodes/$', WorkflowJobNodeFailureNodesList.as_view(), name='workflow_job_node_failure_nodes_list'), re_path(r'^(?P<pk>[0-9]+)/failure_nodes/$', WorkflowJobNodeFailureNodesList.as_view(), name='workflow_job_node_failure_nodes_list'),
url(r'^(?P<pk>[0-9]+)/always_nodes/$', WorkflowJobNodeAlwaysNodesList.as_view(), name='workflow_job_node_always_nodes_list'), re_path(r'^(?P<pk>[0-9]+)/always_nodes/$', WorkflowJobNodeAlwaysNodesList.as_view(), name='workflow_job_node_always_nodes_list'),
url(r'^(?P<pk>[0-9]+)/credentials/$', WorkflowJobNodeCredentialsList.as_view(), name='workflow_job_node_credentials_list'), re_path(r'^(?P<pk>[0-9]+)/credentials/$', WorkflowJobNodeCredentialsList.as_view(), name='workflow_job_node_credentials_list'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import include, url from django.urls import include, re_path
from awx.api.views import ( from awx.api.views import (
WorkflowJobTemplateList, WorkflowJobTemplateList,
@@ -24,39 +24,39 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', WorkflowJobTemplateList.as_view(), name='workflow_job_template_list'), re_path(r'^$', WorkflowJobTemplateList.as_view(), name='workflow_job_template_list'),
url(r'^(?P<pk>[0-9]+)/$', WorkflowJobTemplateDetail.as_view(), name='workflow_job_template_detail'), re_path(r'^(?P<pk>[0-9]+)/$', WorkflowJobTemplateDetail.as_view(), name='workflow_job_template_detail'),
url(r'^(?P<pk>[0-9]+)/workflow_jobs/$', WorkflowJobTemplateJobsList.as_view(), name='workflow_job_template_jobs_list'), re_path(r'^(?P<pk>[0-9]+)/workflow_jobs/$', WorkflowJobTemplateJobsList.as_view(), name='workflow_job_template_jobs_list'),
url(r'^(?P<pk>[0-9]+)/launch/$', WorkflowJobTemplateLaunch.as_view(), name='workflow_job_template_launch'), re_path(r'^(?P<pk>[0-9]+)/launch/$', WorkflowJobTemplateLaunch.as_view(), name='workflow_job_template_launch'),
url(r'^(?P<pk>[0-9]+)/copy/$', WorkflowJobTemplateCopy.as_view(), name='workflow_job_template_copy'), re_path(r'^(?P<pk>[0-9]+)/copy/$', WorkflowJobTemplateCopy.as_view(), name='workflow_job_template_copy'),
url(r'^(?P<pk>[0-9]+)/schedules/$', WorkflowJobTemplateSchedulesList.as_view(), name='workflow_job_template_schedules_list'), re_path(r'^(?P<pk>[0-9]+)/schedules/$', WorkflowJobTemplateSchedulesList.as_view(), name='workflow_job_template_schedules_list'),
url(r'^(?P<pk>[0-9]+)/survey_spec/$', WorkflowJobTemplateSurveySpec.as_view(), name='workflow_job_template_survey_spec'), re_path(r'^(?P<pk>[0-9]+)/survey_spec/$', WorkflowJobTemplateSurveySpec.as_view(), name='workflow_job_template_survey_spec'),
url(r'^(?P<pk>[0-9]+)/workflow_nodes/$', WorkflowJobTemplateWorkflowNodesList.as_view(), name='workflow_job_template_workflow_nodes_list'), re_path(r'^(?P<pk>[0-9]+)/workflow_nodes/$', WorkflowJobTemplateWorkflowNodesList.as_view(), name='workflow_job_template_workflow_nodes_list'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', WorkflowJobTemplateActivityStreamList.as_view(), name='workflow_job_template_activity_stream_list'), re_path(r'^(?P<pk>[0-9]+)/activity_stream/$', WorkflowJobTemplateActivityStreamList.as_view(), name='workflow_job_template_activity_stream_list'),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_started/$', r'^(?P<pk>[0-9]+)/notification_templates_started/$',
WorkflowJobTemplateNotificationTemplatesStartedList.as_view(), WorkflowJobTemplateNotificationTemplatesStartedList.as_view(),
name='workflow_job_template_notification_templates_started_list', name='workflow_job_template_notification_templates_started_list',
), ),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_error/$', r'^(?P<pk>[0-9]+)/notification_templates_error/$',
WorkflowJobTemplateNotificationTemplatesErrorList.as_view(), WorkflowJobTemplateNotificationTemplatesErrorList.as_view(),
name='workflow_job_template_notification_templates_error_list', name='workflow_job_template_notification_templates_error_list',
), ),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_success/$', r'^(?P<pk>[0-9]+)/notification_templates_success/$',
WorkflowJobTemplateNotificationTemplatesSuccessList.as_view(), WorkflowJobTemplateNotificationTemplatesSuccessList.as_view(),
name='workflow_job_template_notification_templates_success_list', name='workflow_job_template_notification_templates_success_list',
), ),
url( re_path(
r'^(?P<pk>[0-9]+)/notification_templates_approvals/$', r'^(?P<pk>[0-9]+)/notification_templates_approvals/$',
WorkflowJobTemplateNotificationTemplatesApprovalList.as_view(), WorkflowJobTemplateNotificationTemplatesApprovalList.as_view(),
name='workflow_job_template_notification_templates_approvals_list', name='workflow_job_template_notification_templates_approvals_list',
), ),
url(r'^(?P<pk>[0-9]+)/access_list/$', WorkflowJobTemplateAccessList.as_view(), name='workflow_job_template_access_list'), re_path(r'^(?P<pk>[0-9]+)/access_list/$', WorkflowJobTemplateAccessList.as_view(), name='workflow_job_template_access_list'),
url(r'^(?P<pk>[0-9]+)/object_roles/$', WorkflowJobTemplateObjectRolesList.as_view(), name='workflow_job_template_object_roles_list'), re_path(r'^(?P<pk>[0-9]+)/object_roles/$', WorkflowJobTemplateObjectRolesList.as_view(), name='workflow_job_template_object_roles_list'),
url(r'^(?P<pk>[0-9]+)/labels/$', WorkflowJobTemplateLabelList.as_view(), name='workflow_job_template_label_list'), re_path(r'^(?P<pk>[0-9]+)/labels/$', WorkflowJobTemplateLabelList.as_view(), name='workflow_job_template_label_list'),
url(r'^(?P<pk>[0-9]+)/', include('awx.api.urls.webhooks'), {'model_kwarg': 'workflow_job_templates'}), re_path(r'^(?P<pk>[0-9]+)/', include('awx.api.urls.webhooks'), {'model_kwarg': 'workflow_job_templates'}),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2017 Ansible, Inc. # Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.conf.urls import url from django.urls import re_path
from awx.api.views import ( from awx.api.views import (
WorkflowJobTemplateNodeList, WorkflowJobTemplateNodeList,
@@ -15,13 +15,13 @@ from awx.api.views import (
urls = [ urls = [
url(r'^$', WorkflowJobTemplateNodeList.as_view(), name='workflow_job_template_node_list'), re_path(r'^$', WorkflowJobTemplateNodeList.as_view(), name='workflow_job_template_node_list'),
url(r'^(?P<pk>[0-9]+)/$', WorkflowJobTemplateNodeDetail.as_view(), name='workflow_job_template_node_detail'), re_path(r'^(?P<pk>[0-9]+)/$', WorkflowJobTemplateNodeDetail.as_view(), name='workflow_job_template_node_detail'),
url(r'^(?P<pk>[0-9]+)/success_nodes/$', WorkflowJobTemplateNodeSuccessNodesList.as_view(), name='workflow_job_template_node_success_nodes_list'), re_path(r'^(?P<pk>[0-9]+)/success_nodes/$', WorkflowJobTemplateNodeSuccessNodesList.as_view(), name='workflow_job_template_node_success_nodes_list'),
url(r'^(?P<pk>[0-9]+)/failure_nodes/$', WorkflowJobTemplateNodeFailureNodesList.as_view(), name='workflow_job_template_node_failure_nodes_list'), re_path(r'^(?P<pk>[0-9]+)/failure_nodes/$', WorkflowJobTemplateNodeFailureNodesList.as_view(), name='workflow_job_template_node_failure_nodes_list'),
url(r'^(?P<pk>[0-9]+)/always_nodes/$', WorkflowJobTemplateNodeAlwaysNodesList.as_view(), name='workflow_job_template_node_always_nodes_list'), re_path(r'^(?P<pk>[0-9]+)/always_nodes/$', WorkflowJobTemplateNodeAlwaysNodesList.as_view(), name='workflow_job_template_node_always_nodes_list'),
url(r'^(?P<pk>[0-9]+)/credentials/$', WorkflowJobTemplateNodeCredentialsList.as_view(), name='workflow_job_template_node_credentials_list'), re_path(r'^(?P<pk>[0-9]+)/credentials/$', WorkflowJobTemplateNodeCredentialsList.as_view(), name='workflow_job_template_node_credentials_list'),
url(r'^(?P<pk>[0-9]+)/create_approval_template/$', WorkflowJobTemplateNodeCreateApproval.as_view(), name='workflow_job_template_node_create_approval'), re_path(r'^(?P<pk>[0-9]+)/create_approval_template/$', WorkflowJobTemplateNodeCreateApproval.as_view(), name='workflow_job_template_node_create_approval'),
] ]
__all__ = ['urls'] __all__ = ['urls']

View File

@@ -29,7 +29,7 @@ from django.views.decorators.csrf import csrf_exempt
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.http import HttpResponse from django.http import HttpResponse
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
# Django REST Framework # Django REST Framework
@@ -105,7 +105,6 @@ from awx.api.permissions import (
ProjectUpdatePermission, ProjectUpdatePermission,
InventoryInventorySourcesUpdatePermission, InventoryInventorySourcesUpdatePermission,
UserPermission, UserPermission,
InstanceGroupTowerPermission,
VariableDataPermission, VariableDataPermission,
WorkflowApprovalPermission, WorkflowApprovalPermission,
IsSystemAdminOrAuditor, IsSystemAdminOrAuditor,
@@ -113,7 +112,7 @@ from awx.api.permissions import (
from awx.api import renderers from awx.api import renderers
from awx.api import serializers from awx.api import serializers
from awx.api.metadata import RoleMetadata from awx.api.metadata import RoleMetadata
from awx.main.constants import ACTIVE_STATES from awx.main.constants import ACTIVE_STATES, SURVEY_TYPE_MAPPING
from awx.main.scheduler.dag_workflow import WorkflowDAG from awx.main.scheduler.dag_workflow import WorkflowDAG
from awx.api.views.mixin import ( from awx.api.views.mixin import (
ControlledByScmMixin, ControlledByScmMixin,
@@ -480,7 +479,6 @@ class InstanceGroupDetail(RelatedJobsPreventDeleteMixin, RetrieveUpdateDestroyAP
name = _("Instance Group Detail") name = _("Instance Group Detail")
model = models.InstanceGroup model = models.InstanceGroup
serializer_class = serializers.InstanceGroupSerializer serializer_class = serializers.InstanceGroupSerializer
permission_classes = (InstanceGroupTowerPermission,)
def update_raw_data(self, data): def update_raw_data(self, data):
if self.get_object().is_container_group: if self.get_object().is_container_group:
@@ -2468,8 +2466,6 @@ class JobTemplateSurveySpec(GenericAPIView):
obj_permission_type = 'admin' obj_permission_type = 'admin'
serializer_class = serializers.EmptySerializer serializer_class = serializers.EmptySerializer
ALLOWED_TYPES = {'text': str, 'textarea': str, 'password': str, 'multiplechoice': str, 'multiselect': str, 'integer': int, 'float': float}
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
obj = self.get_object() obj = self.get_object()
return Response(obj.display_survey_spec()) return Response(obj.display_survey_spec())
@@ -2540,17 +2536,17 @@ class JobTemplateSurveySpec(GenericAPIView):
# Type-specific validation # Type-specific validation
# validate question type <-> default type # validate question type <-> default type
qtype = survey_item["type"] qtype = survey_item["type"]
if qtype not in JobTemplateSurveySpec.ALLOWED_TYPES: if qtype not in SURVEY_TYPE_MAPPING:
return Response( return Response(
dict( dict(
error=_("'{survey_item[type]}' in survey question {idx} is not one of '{allowed_types}' allowed question types.").format( error=_("'{survey_item[type]}' in survey question {idx} is not one of '{allowed_types}' allowed question types.").format(
allowed_types=', '.join(JobTemplateSurveySpec.ALLOWED_TYPES.keys()), **context allowed_types=', '.join(SURVEY_TYPE_MAPPING.keys()), **context
) )
), ),
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
if 'default' in survey_item and survey_item['default'] != '': if 'default' in survey_item and survey_item['default'] != '':
if not isinstance(survey_item['default'], JobTemplateSurveySpec.ALLOWED_TYPES[qtype]): if not isinstance(survey_item['default'], SURVEY_TYPE_MAPPING[qtype]):
type_label = 'string' type_label = 'string'
if qtype in ['integer', 'float']: if qtype in ['integer', 'float']:
type_label = qtype type_label = qtype

View File

@@ -8,7 +8,7 @@ import logging
from django.conf import settings from django.conf import settings
from django.db.models import Q from django.db.models import Q
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
# Django REST Framework # Django REST Framework
from rest_framework.exceptions import PermissionDenied from rest_framework.exceptions import PermissionDenied

View File

@@ -1,7 +1,7 @@
# Copyright (c) 2018 Red Hat, Inc. # Copyright (c) 2018 Red Hat, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from awx.api.generics import APIView, Response from awx.api.generics import APIView, Response
from awx.api.permissions import IsSystemAdminOrAuditor from awx.api.permissions import IsSystemAdminOrAuditor
@@ -19,7 +19,7 @@ class MeshVisualizer(APIView):
data = { data = {
'nodes': InstanceNodeSerializer(Instance.objects.all(), many=True).data, 'nodes': InstanceNodeSerializer(Instance.objects.all(), many=True).data,
'links': InstanceLinkSerializer(InstanceLink.objects.all(), many=True).data, 'links': InstanceLinkSerializer(InstanceLink.objects.select_related('target', 'source'), many=True).data,
} }
return Response(data) return Response(data)

View File

@@ -5,7 +5,7 @@
import logging import logging
# Django # Django
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
# Django REST Framework # Django REST Framework
from rest_framework.response import Response from rest_framework.response import Response

View File

@@ -8,7 +8,7 @@ from django.db.models import Count
from django.db import transaction from django.db import transaction
from django.shortcuts import get_object_or_404 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 gettext_lazy as _
from rest_framework.permissions import SAFE_METHODS from rest_framework.permissions import SAFE_METHODS
from rest_framework.exceptions import PermissionDenied from rest_framework.exceptions import PermissionDenied

View File

@@ -7,7 +7,7 @@ import logging
# Django # Django
from django.db.models import Count from django.db.models import Count
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
# AWX # AWX
from awx.main.models import ( from awx.main.models import (

View File

@@ -8,11 +8,11 @@ import operator
from collections import OrderedDict from collections import OrderedDict
from django.conf import settings from django.conf import settings
from django.utils.encoding import smart_text from django.utils.encoding import smart_str
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views.decorators.csrf import ensure_csrf_cookie from django.views.decorators.csrf import ensure_csrf_cookie
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
@@ -205,7 +205,7 @@ class ApiV2SubscriptionView(APIView):
elif isinstance(exc, (ValueError, OSError)) and exc.args: elif isinstance(exc, (ValueError, OSError)) and exc.args:
msg = exc.args[0] msg = exc.args[0]
else: else:
logger.exception(smart_text(u"Invalid subscription submitted."), extra=dict(actor=request.user.username)) logger.exception(smart_str(u"Invalid subscription submitted."), extra=dict(actor=request.user.username))
return Response({"error": msg}, status=status.HTTP_400_BAD_REQUEST) return Response({"error": msg}, status=status.HTTP_400_BAD_REQUEST)
return Response(validated) return Response(validated)
@@ -246,7 +246,7 @@ class ApiV2AttachView(APIView):
elif isinstance(exc, (ValueError, OSError)) and exc.args: elif isinstance(exc, (ValueError, OSError)) and exc.args:
msg = exc.args[0] msg = exc.args[0]
else: else:
logger.exception(smart_text(u"Invalid subscription submitted."), extra=dict(actor=request.user.username)) logger.exception(smart_str(u"Invalid subscription submitted."), extra=dict(actor=request.user.username))
return Response({"error": msg}, status=status.HTTP_400_BAD_REQUEST) return Response({"error": msg}, status=status.HTTP_400_BAD_REQUEST)
for sub in validated: for sub in validated:
if sub['pool_id'] == pool_id: if sub['pool_id'] == pool_id:
@@ -322,7 +322,7 @@ class ApiV2ConfigView(APIView):
try: try:
data_actual = json.dumps(request.data) data_actual = json.dumps(request.data)
except Exception: except Exception:
logger.info(smart_text(u"Invalid JSON submitted for license."), extra=dict(actor=request.user.username)) logger.info(smart_str(u"Invalid JSON submitted for license."), extra=dict(actor=request.user.username))
return Response({"error": _("Invalid JSON")}, status=status.HTTP_400_BAD_REQUEST) return Response({"error": _("Invalid JSON")}, status=status.HTTP_400_BAD_REQUEST)
license_data = json.loads(data_actual) license_data = json.loads(data_actual)
@@ -346,7 +346,7 @@ class ApiV2ConfigView(APIView):
try: try:
license_data_validated = get_licenser().license_from_manifest(license_data) license_data_validated = get_licenser().license_from_manifest(license_data)
except Exception: except Exception:
logger.warning(smart_text(u"Invalid subscription submitted."), extra=dict(actor=request.user.username)) logger.warning(smart_str(u"Invalid subscription submitted."), extra=dict(actor=request.user.username))
return Response({"error": _("Invalid License")}, status=status.HTTP_400_BAD_REQUEST) return Response({"error": _("Invalid License")}, status=status.HTTP_400_BAD_REQUEST)
else: else:
license_data_validated = get_licenser().validate() license_data_validated = get_licenser().validate()
@@ -357,7 +357,7 @@ class ApiV2ConfigView(APIView):
settings.TOWER_URL_BASE = "{}://{}".format(request.scheme, request.get_host()) settings.TOWER_URL_BASE = "{}://{}".format(request.scheme, request.get_host())
return Response(license_data_validated) return Response(license_data_validated)
logger.warning(smart_text(u"Invalid subscription submitted."), extra=dict(actor=request.user.username)) logger.warning(smart_str(u"Invalid subscription submitted."), extra=dict(actor=request.user.username))
return Response({"error": _("Invalid subscription")}, status=status.HTTP_400_BAD_REQUEST) return Response({"error": _("Invalid subscription")}, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request): def delete(self, request):

View File

@@ -4,7 +4,7 @@ import logging
import urllib.parse import urllib.parse
from django.utils.encoding import force_bytes from django.utils.encoding import force_bytes
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from rest_framework import status from rest_framework import status

View File

@@ -7,8 +7,6 @@ from django.utils.module_loading import autodiscover_modules
# AWX # AWX
from .registry import settings_registry from .registry import settings_registry
default_app_config = 'awx.conf.apps.ConfConfig'
def register(setting, **kwargs): def register(setting, **kwargs):
settings_registry.register(setting, **kwargs) settings_registry.register(setting, **kwargs)

View File

@@ -1,8 +1,10 @@
import sys
# Django # Django
from django.apps import AppConfig from django.apps import AppConfig
# from django.core import checks # from django.core import checks
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
class ConfConfig(AppConfig): class ConfConfig(AppConfig):
@@ -12,6 +14,9 @@ class ConfConfig(AppConfig):
def ready(self): def ready(self):
self.module.autodiscover() self.module.autodiscover()
from .settings import SettingsWrapper
SettingsWrapper.initialize() if not set(sys.argv) & {'migrate', 'check_migrations'}:
from .settings import SettingsWrapper
SettingsWrapper.initialize()

View File

@@ -1,6 +1,6 @@
# Django # Django
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
# AWX # AWX
from awx.conf import fields, register from awx.conf import fields, register

View File

@@ -7,10 +7,10 @@ from collections import OrderedDict
# Django # Django
from django.core.validators import URLValidator, _lazy_re_compile from django.core.validators import URLValidator, _lazy_re_compile
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
# Django REST Framework # Django REST Framework
from rest_framework.fields import BooleanField, CharField, ChoiceField, DictField, DateTimeField, EmailField, IntegerField, ListField, NullBooleanField # noqa from rest_framework.fields import BooleanField, CharField, ChoiceField, DictField, DateTimeField, EmailField, IntegerField, ListField # noqa
from rest_framework.serializers import PrimaryKeyRelatedField # noqa from rest_framework.serializers import PrimaryKeyRelatedField # noqa
# AWX # AWX
@@ -65,11 +65,11 @@ class StringListBooleanField(ListField):
try: try:
if isinstance(value, (list, tuple)): if isinstance(value, (list, tuple)):
return super(StringListBooleanField, self).to_representation(value) return super(StringListBooleanField, self).to_representation(value)
elif value in NullBooleanField.TRUE_VALUES: elif value in BooleanField.TRUE_VALUES:
return True return True
elif value in NullBooleanField.FALSE_VALUES: elif value in BooleanField.FALSE_VALUES:
return False return False
elif value in NullBooleanField.NULL_VALUES: elif value in BooleanField.NULL_VALUES:
return None return None
elif isinstance(value, str): elif isinstance(value, str):
return self.child.to_representation(value) return self.child.to_representation(value)
@@ -82,11 +82,11 @@ class StringListBooleanField(ListField):
try: try:
if isinstance(data, (list, tuple)): if isinstance(data, (list, tuple)):
return super(StringListBooleanField, self).to_internal_value(data) return super(StringListBooleanField, self).to_internal_value(data)
elif data in NullBooleanField.TRUE_VALUES: elif data in BooleanField.TRUE_VALUES:
return True return True
elif data in NullBooleanField.FALSE_VALUES: elif data in BooleanField.FALSE_VALUES:
return False return False
elif data in NullBooleanField.NULL_VALUES: elif data in BooleanField.NULL_VALUES:
return None return None
elif isinstance(data, str): elif isinstance(data, str):
return self.child.run_validation(data) return self.child.run_validation(data)

View File

@@ -2,7 +2,6 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations, models from django.db import migrations, models
import jsonfield.fields
from django.conf import settings from django.conf import settings
@@ -18,7 +17,7 @@ class Migration(migrations.Migration):
('created', models.DateTimeField(default=None, editable=False)), ('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)), ('modified', models.DateTimeField(default=None, editable=False)),
('key', models.CharField(max_length=255)), ('key', models.CharField(max_length=255)),
('value', jsonfield.fields.JSONField(null=True)), ('value', models.JSONField(null=True)),
( (
'user', 'user',
models.ForeignKey(related_name='settings', default=None, editable=False, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True), models.ForeignKey(related_name='settings', default=None, editable=False, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True),

View File

@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import migrations from django.db import migrations, models
import awx.main.fields
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [('conf', '0002_v310_copy_tower_settings')] dependencies = [('conf', '0002_v310_copy_tower_settings')]
operations = [migrations.AlterField(model_name='setting', name='value', field=awx.main.fields.JSONField(null=True))] operations = [migrations.AlterField(model_name='setting', name='value', field=models.JSONField(null=True))]

View File

@@ -5,7 +5,7 @@ from django.utils.timezone import now
def fill_ldap_group_type_params(apps, schema_editor): def fill_ldap_group_type_params(apps, schema_editor):
group_type = settings.AUTH_LDAP_GROUP_TYPE group_type = getattr(settings, 'AUTH_LDAP_GROUP_TYPE', None)
Setting = apps.get_model('conf', 'Setting') Setting = apps.get_model('conf', 'Setting')
group_type_params = {'name_attr': 'cn', 'member_attr': 'member'} group_type_params = {'name_attr': 'cn', 'member_attr': 'member'}
@@ -17,7 +17,7 @@ def fill_ldap_group_type_params(apps, schema_editor):
else: else:
entry = Setting(key='AUTH_LDAP_GROUP_TYPE_PARAMS', value=group_type_params, created=now(), modified=now()) entry = Setting(key='AUTH_LDAP_GROUP_TYPE_PARAMS', value=group_type_params, created=now(), modified=now())
init_attrs = set(inspect.getargspec(group_type.__init__).args[1:]) init_attrs = set(inspect.getfullargspec(group_type.__init__).args[1:])
for k in list(group_type_params.keys()): for k in list(group_type_params.keys()):
if k not in init_attrs: if k not in init_attrs:
del group_type_params[k] del group_type_params[k]

View File

@@ -9,7 +9,6 @@ from django.db import models
# AWX # AWX
from awx.main.models.base import CreatedModifiedModel, prevent_search from awx.main.models.base import CreatedModifiedModel, prevent_search
from awx.main.fields import JSONField
from awx.main.utils import encrypt_field from awx.main.utils import encrypt_field
from awx.conf import settings_registry from awx.conf import settings_registry
@@ -19,7 +18,7 @@ __all__ = ['Setting']
class Setting(CreatedModifiedModel): class Setting(CreatedModifiedModel):
key = models.CharField(max_length=255) key = models.CharField(max_length=255)
value = JSONField(null=True) value = models.JSONField(null=True)
user = prevent_search(models.ForeignKey('auth.User', related_name='settings', default=None, null=True, editable=False, on_delete=models.CASCADE)) user = prevent_search(models.ForeignKey('auth.User', related_name='settings', default=None, null=True, editable=False, on_delete=models.CASCADE))
def __str__(self): def __str__(self):

View File

@@ -8,7 +8,7 @@ import logging
# Django # Django
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.text import slugify from django.utils.text import slugify
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from awx.conf.license import get_license from awx.conf.license import get_license

View File

@@ -6,7 +6,7 @@ from uuid import uuid4
from django.conf import LazySettings from django.conf import LazySettings
from django.core.cache.backends.locmem import LocMemCache from django.core.cache.backends.locmem import LocMemCache
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework.fields import empty from rest_framework.fields import empty
import pytest import pytest

View File

@@ -11,7 +11,7 @@ import time
from django.conf import LazySettings from django.conf import LazySettings
from django.core.cache.backends.locmem import LocMemCache from django.core.cache.backends.locmem import LocMemCache
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
import pytest import pytest
from awx.conf import models, fields from awx.conf import models, fields

View File

@@ -1,13 +1,13 @@
# Copyright (c) 2016 Ansible, Inc. # Copyright (c) 2016 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
from django.urls import re_path
from django.conf.urls import url
from awx.conf.views import SettingCategoryList, SettingSingletonDetail, SettingLoggingTest from awx.conf.views import SettingCategoryList, SettingSingletonDetail, SettingLoggingTest
urlpatterns = [ urlpatterns = [
url(r'^$', SettingCategoryList.as_view(), name='setting_category_list'), re_path(r'^$', SettingCategoryList.as_view(), name='setting_category_list'),
url(r'^(?P<category_slug>[a-z0-9-]+)/$', SettingSingletonDetail.as_view(), name='setting_singleton_detail'), re_path(r'^(?P<category_slug>[a-z0-9-]+)/$', SettingSingletonDetail.as_view(), name='setting_singleton_detail'),
url(r'^logging/test/$', SettingLoggingTest.as_view(), name='setting_logging_test'), re_path(r'^logging/test/$', SettingLoggingTest.as_view(), name='setting_logging_test'),
] ]

View File

@@ -13,7 +13,7 @@ from socket import SHUT_RDWR
from django.db import connection from django.db import connection
from django.conf import settings from django.conf import settings
from django.http import Http404 from django.http import Http404
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
# Django REST Framework # Django REST Framework
from rest_framework.exceptions import PermissionDenied from rest_framework.exceptions import PermissionDenied

View File

@@ -1,4 +1,2 @@
# Copyright (c) 2015 Ansible, Inc. # Copyright (c) 2015 Ansible, Inc.
# All Rights Reserved. # All Rights Reserved.
default_app_config = 'awx.main.apps.MainConfig'

View File

@@ -11,7 +11,7 @@ from functools import reduce
from django.conf import settings from django.conf import settings
from django.db.models import Q, Prefetch from django.db.models import Q, Prefetch
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
# Django REST Framework # Django REST Framework
@@ -465,7 +465,7 @@ class BaseAccess(object):
if display_method == 'schedule': if display_method == 'schedule':
user_capabilities['schedule'] = user_capabilities['start'] user_capabilities['schedule'] = user_capabilities['start']
continue continue
elif display_method == 'delete' and not isinstance(obj, (User, UnifiedJob, CredentialInputSource, ExecutionEnvironment)): elif display_method == 'delete' and not isinstance(obj, (User, UnifiedJob, CredentialInputSource, ExecutionEnvironment, InstanceGroup)):
user_capabilities['delete'] = user_capabilities['edit'] user_capabilities['delete'] = user_capabilities['edit']
continue continue
elif display_method == 'copy' and isinstance(obj, (Group, Host)): elif display_method == 'copy' and isinstance(obj, (Group, Host)):
@@ -575,6 +575,11 @@ class InstanceGroupAccess(BaseAccess):
def can_change(self, obj, data): def can_change(self, obj, data):
return self.user.is_superuser return self.user.is_superuser
def can_delete(self, obj):
if obj.name in [settings.DEFAULT_EXECUTION_QUEUE_NAME, settings.DEFAULT_CONTROL_PLANE_QUEUE_NAME]:
return False
return self.user.is_superuser
class UserAccess(BaseAccess): class UserAccess(BaseAccess):
""" """

View File

@@ -89,7 +89,7 @@ class BroadcastWebsocketStatsManager:
await asyncio.sleep(settings.BROADCAST_WEBSOCKET_STATS_POLL_RATE_SECONDS) await asyncio.sleep(settings.BROADCAST_WEBSOCKET_STATS_POLL_RATE_SECONDS)
except Exception as e: except Exception as e:
logger.warn(e) logger.warning(e)
await asyncio.sleep(settings.BROADCAST_WEBSOCKET_STATS_POLL_RATE_SECONDS) await asyncio.sleep(settings.BROADCAST_WEBSOCKET_STATS_POLL_RATE_SECONDS)
self.start() self.start()

View File

@@ -10,7 +10,7 @@ from django.db.models import Count
from django.conf import settings from django.conf import settings
from django.contrib.sessions.models import Session from django.contrib.sessions.models import Session
from django.utils.timezone import now, timedelta from django.utils.timezone import now, timedelta
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from psycopg2.errors import UntranslatableCharacter from psycopg2.errors import UntranslatableCharacter
@@ -337,6 +337,7 @@ def _events_table(since, full_path, until, tbl, where_column, project_job_create
{tbl}.parent_uuid, {tbl}.parent_uuid,
{tbl}.event, {tbl}.event,
task_action, task_action,
resolved_action,
-- '-' operator listed here: -- '-' operator listed here:
-- https://www.postgresql.org/docs/12/functions-json.html -- https://www.postgresql.org/docs/12/functions-json.html
-- note that operator is only supported by jsonb objects -- note that operator is only supported by jsonb objects
@@ -356,7 +357,7 @@ def _events_table(since, full_path, until, tbl, where_column, project_job_create
x.duration AS duration, x.duration AS duration,
x.res->'warnings' AS warnings, x.res->'warnings' AS warnings,
x.res->'deprecations' AS deprecations x.res->'deprecations' AS deprecations
FROM {tbl}, jsonb_to_record({event_data}) AS x("res" json, "duration" text, "task_action" text, "start" text, "end" text) FROM {tbl}, jsonb_to_record({event_data}) AS x("res" json, "duration" text, "task_action" text, "resolved_action" text, "start" text, "end" text)
WHERE ({tbl}.{where_column} > '{since.isoformat()}' AND {tbl}.{where_column} <= '{until.isoformat()}')) TO STDOUT WITH CSV HEADER''' WHERE ({tbl}.{where_column} > '{since.isoformat()}' AND {tbl}.{where_column} <= '{until.isoformat()}')) TO STDOUT WITH CSV HEADER'''
return query return query
@@ -366,23 +367,24 @@ def _events_table(since, full_path, until, tbl, where_column, project_job_create
return _copy_table(table='events', query=query(f"replace({tbl}.event_data::text, '\\u0000', '')::jsonb"), path=full_path) return _copy_table(table='events', query=query(f"replace({tbl}.event_data::text, '\\u0000', '')::jsonb"), path=full_path)
@register('events_table', '1.3', format='csv', description=_('Automation task records'), expensive=four_hour_slicing) @register('events_table', '1.4', format='csv', description=_('Automation task records'), expensive=four_hour_slicing)
def events_table_unpartitioned(since, full_path, until, **kwargs): def events_table_unpartitioned(since, full_path, until, **kwargs):
return _events_table(since, full_path, until, '_unpartitioned_main_jobevent', 'created', **kwargs) return _events_table(since, full_path, until, '_unpartitioned_main_jobevent', 'created', **kwargs)
@register('events_table', '1.3', format='csv', description=_('Automation task records'), expensive=four_hour_slicing) @register('events_table', '1.4', format='csv', description=_('Automation task records'), expensive=four_hour_slicing)
def events_table_partitioned_modified(since, full_path, until, **kwargs): def events_table_partitioned_modified(since, full_path, until, **kwargs):
return _events_table(since, full_path, until, 'main_jobevent', 'modified', project_job_created=True, **kwargs) return _events_table(since, full_path, until, 'main_jobevent', 'modified', project_job_created=True, **kwargs)
@register('unified_jobs_table', '1.2', format='csv', description=_('Data on jobs run'), expensive=four_hour_slicing) @register('unified_jobs_table', '1.3', format='csv', description=_('Data on jobs run'), expensive=four_hour_slicing)
def unified_jobs_table(since, full_path, until, **kwargs): def unified_jobs_table(since, full_path, until, **kwargs):
unified_job_query = '''COPY (SELECT main_unifiedjob.id, unified_job_query = '''COPY (SELECT main_unifiedjob.id,
main_unifiedjob.polymorphic_ctype_id, main_unifiedjob.polymorphic_ctype_id,
django_content_type.model, django_content_type.model,
main_unifiedjob.organization_id, main_unifiedjob.organization_id,
main_organization.name as organization_name, main_organization.name as organization_name,
main_executionenvironment.image as execution_environment_image,
main_job.inventory_id, main_job.inventory_id,
main_inventory.name as inventory_name, main_inventory.name as inventory_name,
main_unifiedjob.created, main_unifiedjob.created,
@@ -407,6 +409,7 @@ def unified_jobs_table(since, full_path, until, **kwargs):
LEFT JOIN main_job ON main_unifiedjob.id = main_job.unifiedjob_ptr_id LEFT JOIN main_job ON main_unifiedjob.id = main_job.unifiedjob_ptr_id
LEFT JOIN main_inventory ON main_job.inventory_id = main_inventory.id LEFT JOIN main_inventory ON main_job.inventory_id = main_inventory.id
LEFT JOIN main_organization ON main_organization.id = main_unifiedjob.organization_id LEFT JOIN main_organization ON main_organization.id = main_unifiedjob.organization_id
LEFT JOIN main_executionenvironment ON main_executionenvironment.id = main_unifiedjob.execution_environment_id
WHERE ((main_unifiedjob.created > '{0}' AND main_unifiedjob.created <= '{1}') WHERE ((main_unifiedjob.created > '{0}' AND main_unifiedjob.created <= '{1}')
OR (main_unifiedjob.finished > '{0}' AND main_unifiedjob.finished <= '{1}')) OR (main_unifiedjob.finished > '{0}' AND main_unifiedjob.finished <= '{1}'))
AND main_unifiedjob.launch_type != 'sync' AND main_unifiedjob.launch_type != 'sync'
@@ -417,11 +420,12 @@ def unified_jobs_table(since, full_path, until, **kwargs):
return _copy_table(table='unified_jobs', query=unified_job_query, path=full_path) return _copy_table(table='unified_jobs', query=unified_job_query, path=full_path)
@register('unified_job_template_table', '1.0', format='csv', description=_('Data on job templates')) @register('unified_job_template_table', '1.1', format='csv', description=_('Data on job templates'))
def unified_job_template_table(since, full_path, **kwargs): def unified_job_template_table(since, full_path, **kwargs):
unified_job_template_query = '''COPY (SELECT main_unifiedjobtemplate.id, unified_job_template_query = '''COPY (SELECT main_unifiedjobtemplate.id,
main_unifiedjobtemplate.polymorphic_ctype_id, main_unifiedjobtemplate.polymorphic_ctype_id,
django_content_type.model, django_content_type.model,
main_executionenvironment.image as execution_environment_image,
main_unifiedjobtemplate.created, main_unifiedjobtemplate.created,
main_unifiedjobtemplate.modified, main_unifiedjobtemplate.modified,
main_unifiedjobtemplate.created_by_id, main_unifiedjobtemplate.created_by_id,
@@ -434,7 +438,8 @@ def unified_job_template_table(since, full_path, **kwargs):
main_unifiedjobtemplate.next_job_run, main_unifiedjobtemplate.next_job_run,
main_unifiedjobtemplate.next_schedule_id, main_unifiedjobtemplate.next_schedule_id,
main_unifiedjobtemplate.status main_unifiedjobtemplate.status
FROM main_unifiedjobtemplate, django_content_type FROM main_unifiedjobtemplate
LEFT JOIN main_executionenvironment ON main_executionenvironment.id = main_unifiedjobtemplate.execution_environment_id, django_content_type
WHERE main_unifiedjobtemplate.polymorphic_ctype_id = django_content_type.id WHERE main_unifiedjobtemplate.polymorphic_ctype_id = django_content_type.id
ORDER BY main_unifiedjobtemplate.id ASC) TO STDOUT WITH CSV HEADER''' ORDER BY main_unifiedjobtemplate.id ASC) TO STDOUT WITH CSV HEADER'''
return _copy_table(table='unified_job_template', query=unified_job_template_query, path=full_path) return _copy_table(table='unified_job_template', query=unified_job_template_query, path=full_path)

View File

@@ -1,5 +1,5 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
class MainConfig(AppConfig): class MainConfig(AppConfig):

View File

@@ -2,7 +2,7 @@
import logging import logging
# Django # Django
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
# Django REST Framework # Django REST Framework
from rest_framework import serializers from rest_framework import serializers
@@ -334,6 +334,19 @@ register(
category_slug='jobs', category_slug='jobs',
) )
register(
'AWX_MOUNT_ISOLATED_PATHS_ON_K8S',
field_class=fields.BooleanField,
default=False,
label=_('Expose host paths for Container Groups'),
help_text=_(
'Expose paths via hostPath for the Pods created by a Container Group. '
'HostPath volumes present many security risks, and it is a best practice to avoid the use of HostPaths when possible. '
),
category=_('Jobs'),
category_slug='jobs',
)
register( register(
'GALAXY_IGNORE_CERTS', 'GALAXY_IGNORE_CERTS',
field_class=fields.BooleanField, field_class=fields.BooleanField,

View File

@@ -3,7 +3,7 @@
import re import re
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
__all__ = [ __all__ = [
'CLOUD_PROVIDERS', 'CLOUD_PROVIDERS',
@@ -88,7 +88,10 @@ JOB_FOLDER_PREFIX = 'awx_%s_'
# :z option tells Podman that two containers share the volume content with r/w # :z option tells Podman that two containers share the volume content with r/w
# :O option tells Podman to mount the directory from the host as a temporary storage using the overlay file system. # :O option tells Podman to mount the directory from the host as a temporary storage using the overlay file system.
# :ro or :rw option to mount a volume in read-only or read-write mode, respectively. By default, the volumes are mounted read-write.
# see podman-run manpage for further details # see podman-run manpage for further details
# /HOST-DIR:/CONTAINER-DIR:OPTIONS # /HOST-DIR:/CONTAINER-DIR:OPTIONS
CONTAINER_VOLUMES_MOUNT_TYPES = ['z', 'O'] CONTAINER_VOLUMES_MOUNT_TYPES = ['z', 'O', 'ro', 'rw']
MAX_ISOLATED_PATH_COLON_DELIMITER = 2 MAX_ISOLATED_PATH_COLON_DELIMITER = 2
SURVEY_TYPE_MAPPING = {'text': str, 'textarea': str, 'password': str, 'multiplechoice': str, 'multiselect': str, 'integer': int, 'float': (float, int)}

View File

@@ -65,7 +65,7 @@ class WebsocketSecretAuthHelper:
nonce_parsed = int(nonce_parsed) nonce_parsed = int(nonce_parsed)
nonce_diff = now - nonce_parsed nonce_diff = now - nonce_parsed
if abs(nonce_diff) > nonce_tolerance: if abs(nonce_diff) > nonce_tolerance:
logger.warn(f"Potential replay attack or machine(s) time out of sync by {nonce_diff} seconds.") logger.warning(f"Potential replay attack or machine(s) time out of sync by {nonce_diff} seconds.")
raise ValueError(f"Potential replay attack or machine(s) time out of sync by {nonce_diff} seconds.") raise ValueError(f"Potential replay attack or machine(s) time out of sync by {nonce_diff} seconds.")
return True return True
@@ -85,7 +85,7 @@ class BroadcastConsumer(AsyncJsonWebsocketConsumer):
try: try:
WebsocketSecretAuthHelper.is_authorized(self.scope) WebsocketSecretAuthHelper.is_authorized(self.scope)
except Exception: except Exception:
logger.warn(f"client '{self.channel_name}' failed to authorize against the broadcast endpoint.") logger.warning(f"client '{self.channel_name}' failed to authorize against the broadcast endpoint.")
await self.close() await self.close()
return return

View File

@@ -2,7 +2,7 @@ from .plugin import CredentialPlugin, CertFiles, raise_for_status
from urllib.parse import quote, urlencode, urljoin from urllib.parse import quote, urlencode, urljoin
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
import requests import requests
aim_inputs = { aim_inputs = {

View File

@@ -1,6 +1,6 @@
from .plugin import CredentialPlugin from .plugin import CredentialPlugin
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from azure.keyvault import KeyVaultClient, KeyVaultAuthentication from azure.keyvault import KeyVaultClient, KeyVaultAuthentication
from azure.common.credentials import ServicePrincipalCredentials from azure.common.credentials import ServicePrincipalCredentials
from msrestazure import azure_cloud from msrestazure import azure_cloud

View File

@@ -1,115 +1,115 @@
from .plugin import CredentialPlugin, raise_for_status from .plugin import CredentialPlugin, raise_for_status
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from urllib.parse import urljoin from urllib.parse import urljoin
import requests import requests
pas_inputs = { pas_inputs = {
'fields': [ 'fields': [
{ {
'id': 'url', 'id': 'url',
'label': _('Centrify Tenant URL'), 'label': _('Centrify Tenant URL'),
'type': 'string', 'type': 'string',
'help_text': _('Centrify Tenant URL'), 'help_text': _('Centrify Tenant URL'),
'format': 'url', 'format': 'url',
}, },
{ {
'id': 'client_id', 'id': 'client_id',
'label': _('Centrify API User'), 'label': _('Centrify API User'),
'type': 'string', 'type': 'string',
'help_text': _('Centrify API User, having necessary permissions as mentioned in support doc'), 'help_text': _('Centrify API User, having necessary permissions as mentioned in support doc'),
}, },
{ {
'id': 'client_password', 'id': 'client_password',
'label': _('Centrify API Password'), 'label': _('Centrify API Password'),
'type': 'string', 'type': 'string',
'help_text': _('Password of Centrify API User with necessary permissions'), 'help_text': _('Password of Centrify API User with necessary permissions'),
'secret': True, 'secret': True,
}, },
{ {
'id': 'oauth_application_id', 'id': 'oauth_application_id',
'label': _('OAuth2 Application ID'), 'label': _('OAuth2 Application ID'),
'type': 'string', 'type': 'string',
'help_text': _('Application ID of the configured OAuth2 Client (defaults to \'awx\')'), 'help_text': _('Application ID of the configured OAuth2 Client (defaults to \'awx\')'),
'default': 'awx', 'default': 'awx',
}, },
{ {
'id': 'oauth_scope', 'id': 'oauth_scope',
'label': _('OAuth2 Scope'), 'label': _('OAuth2 Scope'),
'type': 'string', 'type': 'string',
'help_text': _('Scope of the configured OAuth2 Client (defaults to \'awx\')'), 'help_text': _('Scope of the configured OAuth2 Client (defaults to \'awx\')'),
'default': 'awx', 'default': 'awx',
}, },
], ],
'metadata': [ 'metadata': [
{ {
'id': 'account-name', 'id': 'account-name',
'label': _('Account Name'), 'label': _('Account Name'),
'type': 'string', 'type': 'string',
'help_text': _('Local system account or Domain account name enrolled in Centrify Vault. eg. (root or DOMAIN/Administrator)'), 'help_text': _('Local system account or Domain account name enrolled in Centrify Vault. eg. (root or DOMAIN/Administrator)'),
}, },
{ {
'id': 'system-name', 'id': 'system-name',
'label': _('System Name'), 'label': _('System Name'),
'type': 'string', 'type': 'string',
'help_text': _('Machine Name enrolled with in Centrify Portal'), 'help_text': _('Machine Name enrolled with in Centrify Portal'),
}, },
], ],
'required': ['url', 'account-name', 'system-name', 'client_id', 'client_password'], 'required': ['url', 'account-name', 'system-name', 'client_id', 'client_password'],
} }
# generate bearer token to authenticate with PAS portal, Input : Client ID, Client Secret # generate bearer token to authenticate with PAS portal, Input : Client ID, Client Secret
def handle_auth(**kwargs): def handle_auth(**kwargs):
post_data = {"grant_type": "client_credentials", "scope": kwargs['oauth_scope']} post_data = {"grant_type": "client_credentials", "scope": kwargs['oauth_scope']}
response = requests.post(kwargs['endpoint'], data=post_data, auth=(kwargs['client_id'], kwargs['client_password']), verify=True, timeout=(5, 30)) response = requests.post(kwargs['endpoint'], data=post_data, auth=(kwargs['client_id'], kwargs['client_password']), verify=True, timeout=(5, 30))
raise_for_status(response) raise_for_status(response)
try: try:
return response.json()['access_token'] return response.json()['access_token']
except KeyError: except KeyError:
raise RuntimeError('OAuth request to tenant was unsuccessful') raise RuntimeError('OAuth request to tenant was unsuccessful')
# fetch the ID of system with RedRock query, Input : System Name, Account Name # fetch the ID of system with RedRock query, Input : System Name, Account Name
def get_ID(**kwargs): def get_ID(**kwargs):
endpoint = urljoin(kwargs['url'], '/Redrock/query') endpoint = urljoin(kwargs['url'], '/Redrock/query')
name = " Name='{0}' and User='{1}'".format(kwargs['system_name'], kwargs['acc_name']) name = " Name='{0}' and User='{1}'".format(kwargs['system_name'], kwargs['acc_name'])
query = 'Select ID from VaultAccount where {0}'.format(name) query = 'Select ID from VaultAccount where {0}'.format(name)
post_headers = {"Authorization": "Bearer " + kwargs['access_token'], "X-CENTRIFY-NATIVE-CLIENT": "true"} post_headers = {"Authorization": "Bearer " + kwargs['access_token'], "X-CENTRIFY-NATIVE-CLIENT": "true"}
response = requests.post(endpoint, json={'Script': query}, headers=post_headers, verify=True, timeout=(5, 30)) response = requests.post(endpoint, json={'Script': query}, headers=post_headers, verify=True, timeout=(5, 30))
raise_for_status(response) raise_for_status(response)
try: try:
result_str = response.json()["Result"]["Results"] result_str = response.json()["Result"]["Results"]
return result_str[0]["Row"]["ID"] return result_str[0]["Row"]["ID"]
except (IndexError, KeyError): except (IndexError, KeyError):
raise RuntimeError("Error Detected!! Check the Inputs") raise RuntimeError("Error Detected!! Check the Inputs")
# CheckOut Password from Centrify Vault, Input : ID # CheckOut Password from Centrify Vault, Input : ID
def get_passwd(**kwargs): def get_passwd(**kwargs):
endpoint = urljoin(kwargs['url'], '/ServerManage/CheckoutPassword') endpoint = urljoin(kwargs['url'], '/ServerManage/CheckoutPassword')
post_headers = {"Authorization": "Bearer " + kwargs['access_token'], "X-CENTRIFY-NATIVE-CLIENT": "true"} post_headers = {"Authorization": "Bearer " + kwargs['access_token'], "X-CENTRIFY-NATIVE-CLIENT": "true"}
response = requests.post(endpoint, json={'ID': kwargs['acc_id']}, headers=post_headers, verify=True, timeout=(5, 30)) response = requests.post(endpoint, json={'ID': kwargs['acc_id']}, headers=post_headers, verify=True, timeout=(5, 30))
raise_for_status(response) raise_for_status(response)
try: try:
return response.json()["Result"]["Password"] return response.json()["Result"]["Password"]
except KeyError: except KeyError:
raise RuntimeError("Password Not Found") raise RuntimeError("Password Not Found")
def centrify_backend(**kwargs): def centrify_backend(**kwargs):
url = kwargs.get('url') url = kwargs.get('url')
acc_name = kwargs.get('account-name') acc_name = kwargs.get('account-name')
system_name = kwargs.get('system-name') system_name = kwargs.get('system-name')
client_id = kwargs.get('client_id') client_id = kwargs.get('client_id')
client_password = kwargs.get('client_password') client_password = kwargs.get('client_password')
app_id = kwargs.get('oauth_application_id', 'awx') app_id = kwargs.get('oauth_application_id', 'awx')
endpoint = urljoin(url, f'/oauth2/token/{app_id}') endpoint = urljoin(url, f'/oauth2/token/{app_id}')
endpoint = {'endpoint': endpoint, 'client_id': client_id, 'client_password': client_password, 'oauth_scope': kwargs.get('oauth_scope', 'awx')} endpoint = {'endpoint': endpoint, 'client_id': client_id, 'client_password': client_password, 'oauth_scope': kwargs.get('oauth_scope', 'awx')}
token = handle_auth(**endpoint) token = handle_auth(**endpoint)
get_id_args = {'system_name': system_name, 'acc_name': acc_name, 'url': url, 'access_token': token} get_id_args = {'system_name': system_name, 'acc_name': acc_name, 'url': url, 'access_token': token}
acc_id = get_ID(**get_id_args) acc_id = get_ID(**get_id_args)
get_pwd_args = {'url': url, 'acc_id': acc_id, 'access_token': token} get_pwd_args = {'url': url, 'acc_id': acc_id, 'access_token': token}
return get_passwd(**get_pwd_args) return get_passwd(**get_pwd_args)
centrify_plugin = CredentialPlugin('Centrify Vault Credential Provider Lookup', inputs=pas_inputs, backend=centrify_backend) centrify_plugin = CredentialPlugin('Centrify Vault Credential Provider Lookup', inputs=pas_inputs, backend=centrify_backend)

View File

@@ -3,7 +3,7 @@ from .plugin import CredentialPlugin, CertFiles, raise_for_status
import base64 import base64
from urllib.parse import urljoin, quote from urllib.parse import urljoin, quote
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
import requests import requests

View File

@@ -1,7 +1,7 @@
from .plugin import CredentialPlugin from .plugin import CredentialPlugin
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from thycotic.secrets.vault import SecretsVault from thycotic.secrets.vault import SecretsVault

View File

@@ -6,7 +6,7 @@ from urllib.parse import urljoin
from .plugin import CredentialPlugin, CertFiles, raise_for_status from .plugin import CredentialPlugin, CertFiles, raise_for_status
import requests import requests
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
base_inputs = { base_inputs = {
'fields': [ 'fields': [

View File

@@ -1,5 +1,5 @@
from .plugin import CredentialPlugin from .plugin import CredentialPlugin
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from thycotic.secrets.server import PasswordGrantAuthorizer, SecretServer, ServerSecret from thycotic.secrets.server import PasswordGrantAuthorizer, SecretServer, ServerSecret

View File

@@ -42,7 +42,7 @@ class Control(object):
return f"reply_to_{str(uuid.uuid4()).replace('-','_')}" return f"reply_to_{str(uuid.uuid4()).replace('-','_')}"
def control_with_reply(self, command, timeout=5): def control_with_reply(self, command, timeout=5):
logger.warn('checking {} {} for {}'.format(self.service, command, self.queuename)) logger.warning('checking {} {} for {}'.format(self.service, command, self.queuename))
reply_queue = Control.generate_reply_queue_name() reply_queue = Control.generate_reply_queue_name()
self.result = None self.result = None

View File

@@ -6,7 +6,8 @@ from multiprocessing import Process
from django.conf import settings from django.conf import settings
from django.db import connections from django.db import connections
from schedule import Scheduler from schedule import Scheduler
from django_guid.middleware import GuidMiddleware from django_guid import set_guid
from django_guid.utils import generate_guid
from awx.main.dispatch.worker import TaskWorker from awx.main.dispatch.worker import TaskWorker
@@ -19,20 +20,20 @@ class Scheduler(Scheduler):
def run(): def run():
ppid = os.getppid() ppid = os.getppid()
logger.warn('periodic beat started') logger.warning('periodic beat started')
while True: while True:
if os.getppid() != ppid: if os.getppid() != ppid:
# if the parent PID changes, this process has been orphaned # if the parent PID changes, this process has been orphaned
# via e.g., segfault or sigkill, we should exit too # via e.g., segfault or sigkill, we should exit too
pid = os.getpid() pid = os.getpid()
logger.warn(f'periodic beat exiting gracefully pid:{pid}') logger.warning(f'periodic beat exiting gracefully pid:{pid}')
raise SystemExit() raise SystemExit()
try: try:
for conn in connections.all(): for conn in connections.all():
# If the database connection has a hiccup, re-establish a new # If the database connection has a hiccup, re-establish a new
# connection # connection
conn.close_if_unusable_or_obsolete() conn.close_if_unusable_or_obsolete()
GuidMiddleware.set_guid(GuidMiddleware._generate_guid()) set_guid(generate_guid())
self.run_pending() self.run_pending()
except Exception: except Exception:
logger.exception('encountered an error while scheduling periodic tasks') logger.exception('encountered an error while scheduling periodic tasks')

View File

@@ -16,13 +16,13 @@ from queue import Full as QueueFull, Empty as QueueEmpty
from django.conf import settings from django.conf import settings
from django.db import connection as django_connection, connections from django.db import connection as django_connection, connections
from django.core.cache import cache as django_cache from django.core.cache import cache as django_cache
from django_guid.middleware import GuidMiddleware from django_guid import set_guid
from jinja2 import Template from jinja2 import Template
import psutil import psutil
from awx.main.models import UnifiedJob from awx.main.models import UnifiedJob
from awx.main.dispatch import reaper from awx.main.dispatch import reaper
from awx.main.utils.common import convert_mem_str_to_bytes from awx.main.utils.common import convert_mem_str_to_bytes, get_mem_effective_capacity
if 'run_callback_receiver' in sys.argv: if 'run_callback_receiver' in sys.argv:
logger = logging.getLogger('awx.main.commands.run_callback_receiver') logger = logging.getLogger('awx.main.commands.run_callback_receiver')
@@ -142,7 +142,7 @@ class PoolWorker(object):
# when this occurs, it's _fine_ to ignore this KeyError because # when this occurs, it's _fine_ to ignore this KeyError because
# the purpose of self.managed_tasks is to just track internal # the purpose of self.managed_tasks is to just track internal
# state of which events are *currently* being processed. # state of which events are *currently* being processed.
logger.warn('Event UUID {} appears to be have been duplicated.'.format(uuid)) logger.warning('Event UUID {} appears to be have been duplicated.'.format(uuid))
@property @property
def current_task(self): def current_task(self):
@@ -291,8 +291,8 @@ class WorkerPool(object):
pass pass
except Exception: except Exception:
tb = traceback.format_exc() tb = traceback.format_exc()
logger.warn("could not write to queue %s" % preferred_queue) logger.warning("could not write to queue %s" % preferred_queue)
logger.warn("detail: {}".format(tb)) logger.warning("detail: {}".format(tb))
write_attempt_order.append(preferred_queue) write_attempt_order.append(preferred_queue)
logger.error("could not write payload to any queue, attempted order: {}".format(write_attempt_order)) logger.error("could not write payload to any queue, attempted order: {}".format(write_attempt_order))
return None return None
@@ -324,8 +324,9 @@ class AutoscalePool(WorkerPool):
total_memory_gb = convert_mem_str_to_bytes(settings_absmem) // 2**30 total_memory_gb = convert_mem_str_to_bytes(settings_absmem) // 2**30
else: else:
total_memory_gb = (psutil.virtual_memory().total >> 30) + 1 # noqa: round up total_memory_gb = (psutil.virtual_memory().total >> 30) + 1 # noqa: round up
# 5 workers per GB of total memory
self.max_workers = total_memory_gb * 5 # Get same number as max forks based on memory, this function takes memory as bytes
self.max_workers = get_mem_effective_capacity(total_memory_gb * 2**30)
# max workers can't be less than min_workers # max workers can't be less than min_workers
self.max_workers = max(self.min_workers, self.max_workers) self.max_workers = max(self.min_workers, self.max_workers)
@@ -435,7 +436,7 @@ class AutoscalePool(WorkerPool):
def write(self, preferred_queue, body): def write(self, preferred_queue, body):
if 'guid' in body: if 'guid' in body:
GuidMiddleware.set_guid(body['guid']) set_guid(body['guid'])
try: try:
# when the cluster heartbeat occurs, clean up internally # when the cluster heartbeat occurs, clean up internally
if isinstance(body, dict) and 'cluster_node_heartbeat' in body['task']: if isinstance(body, dict) and 'cluster_node_heartbeat' in body['task']:

View File

@@ -5,7 +5,7 @@ import json
from uuid import uuid4 from uuid import uuid4
from django.conf import settings from django.conf import settings
from django_guid.middleware import GuidMiddleware from django_guid import get_guid
from . import pg_bus_conn from . import pg_bus_conn
@@ -76,7 +76,7 @@ class task:
logger.error(msg) logger.error(msg)
raise ValueError(msg) raise ValueError(msg)
obj = {'uuid': task_id, 'args': args, 'kwargs': kwargs, 'task': cls.name} obj = {'uuid': task_id, 'args': args, 'kwargs': kwargs, 'task': cls.name}
guid = GuidMiddleware.get_guid() guid = get_guid()
if guid: if guid:
obj['guid'] = guid obj['guid'] = guid
obj.update(**kw) obj.update(**kw)

View File

@@ -60,7 +60,7 @@ class AWXConsumerBase(object):
return f'listening on {self.queues}' return f'listening on {self.queues}'
def control(self, body): def control(self, body):
logger.warn(f'Received control signal:\n{body}') logger.warning(f'Received control signal:\n{body}')
control = body.get('control') control = body.get('control')
if control in ('status', 'running'): if control in ('status', 'running'):
reply_queue = body['reply_to'] reply_queue = body['reply_to']
@@ -118,7 +118,7 @@ class AWXConsumerBase(object):
def stop(self, signum, frame): def stop(self, signum, frame):
self.should_stop = True self.should_stop = True
logger.warn('received {}, stopping'.format(signame(signum))) logger.warning('received {}, stopping'.format(signame(signum)))
self.worker.on_stop() self.worker.on_stop()
raise SystemExit() raise SystemExit()
@@ -153,7 +153,7 @@ class AWXConsumerPG(AWXConsumerBase):
if self.should_stop: if self.should_stop:
return return
except psycopg2.InterfaceError: except psycopg2.InterfaceError:
logger.warn("Stale Postgres message bus connection, reconnecting") logger.warning("Stale Postgres message bus connection, reconnecting")
continue continue

View File

@@ -9,7 +9,7 @@ from django.conf import settings
from django.utils.timezone import now as tz_now from django.utils.timezone import now as tz_now
from django.db import DatabaseError, OperationalError, connection as django_connection from django.db import DatabaseError, OperationalError, connection as django_connection
from django.db.utils import InterfaceError, InternalError from django.db.utils import InterfaceError, InternalError
from django_guid.middleware import GuidMiddleware from django_guid import set_guid
import psutil import psutil
@@ -184,7 +184,7 @@ class CallbackBrokerWorker(BaseWorker):
if body.get('event') == 'EOF': if body.get('event') == 'EOF':
try: try:
if 'guid' in body: if 'guid' in body:
GuidMiddleware.set_guid(body['guid']) set_guid(body['guid'])
final_counter = body.get('final_counter', 0) final_counter = body.get('final_counter', 0)
logger.info('Event processing is finished for Job {}, sending notifications'.format(job_identifier)) logger.info('Event processing is finished for Job {}, sending notifications'.format(job_identifier))
# EOF events are sent when stdout for the running task is # EOF events are sent when stdout for the running task is
@@ -208,7 +208,7 @@ class CallbackBrokerWorker(BaseWorker):
logger.exception('Worker failed to emit notifications: Job {}'.format(job_identifier)) logger.exception('Worker failed to emit notifications: Job {}'.format(job_identifier))
finally: finally:
self.subsystem_metrics.inc('callback_receiver_events_in_memory', -1) self.subsystem_metrics.inc('callback_receiver_events_in_memory', -1)
GuidMiddleware.set_guid('') set_guid('')
return return
skip_websocket_message = body.pop('skip_websocket_message', False) skip_websocket_message = body.pop('skip_websocket_message', False)

View File

@@ -7,7 +7,7 @@ import traceback
from kubernetes.config import kube_config from kubernetes.config import kube_config
from django.conf import settings from django.conf import settings
from django_guid.middleware import GuidMiddleware from django_guid import set_guid
from awx.main.tasks.system import dispatch_startup, inform_cluster_of_shutdown from awx.main.tasks.system import dispatch_startup, inform_cluster_of_shutdown
@@ -54,7 +54,7 @@ class TaskWorker(BaseWorker):
args = body.get('args', []) args = body.get('args', [])
kwargs = body.get('kwargs', {}) kwargs = body.get('kwargs', {})
if 'guid' in body: if 'guid' in body:
GuidMiddleware.set_guid(body.pop('guid')) set_guid(body.pop('guid'))
_call = TaskWorker.resolve_callable(task) _call = TaskWorker.resolve_callable(task)
if inspect.isclass(_call): if inspect.isclass(_call):
# the callable is a class, e.g., RunJob; instantiate and # the callable is a class, e.g., RunJob; instantiate and

Some files were not shown because too many files have changed in this diff Show More