diff --git a/lib/main/models/__init__.py b/lib/main/models/__init__.py index 62ff65d93b..57ceb24f5e 100644 --- a/lib/main/models/__init__.py +++ b/lib/main/models/__init__.py @@ -231,8 +231,7 @@ class Tag(models.Model): return unicode(self.name) def get_absolute_url(self): - import lib.urls - return reverse(lib.urls.views_TagsDetail, args=(self.pk,)) + return reverse('main:tags_detail', args=(self.pk,)) @classmethod def can_user_add(cls, user, data): @@ -275,8 +274,7 @@ class Organization(CommonModel): projects = models.ManyToManyField('Project', blank=True, related_name='organizations') def get_absolute_url(self): - import lib.urls - return reverse(lib.urls.views_OrganizationsDetail, args=(self.pk,)) + return reverse('main:organizations_detail', args=(self.pk,)) @classmethod def can_user_delete(cls, user, obj): @@ -316,8 +314,7 @@ class Inventory(CommonModel): organization = models.ForeignKey(Organization, null=False, related_name='inventories') def get_absolute_url(self): - import lib.urls - return reverse(lib.urls.views_InventoryDetail, args=(self.pk,)) + return reverse('main:inventory_detail', args=(self.pk,)) @classmethod def _has_permission_types(cls, user, obj, allowed): @@ -423,8 +420,7 @@ class Host(CommonModelNameNotUnique): return rc def get_absolute_url(self): - import lib.urls - return reverse(lib.urls.views_HostsDetail, args=(self.pk,)) + return reverse('main:hosts_detail', args=(self.pk,)) # Use .job_host_summaries.all() to get jobs affecting this host. # Use .job_events.all() to get events affecting this host. @@ -465,8 +461,7 @@ class Group(CommonModelNameNotUnique): return Inventory.can_user_read(user, obj.inventory) def get_absolute_url(self): - import lib.urls - return reverse(lib.urls.views_GroupsDetail, args=(self.pk,)) + return reverse('main:groups_detail', args=(self.pk,)) # FIXME: audit nullables # FIXME: audit cascades @@ -488,8 +483,7 @@ class VariableData(CommonModelNameNotUnique): return '%s = %s' % (self.name, self.data) def get_absolute_url(self): - import lib.urls - return reverse(lib.urls.views_VariableDetail, args=(self.pk,)) + return reverse('main:variable_detail', args=(self.pk,)) @classmethod def can_user_read(cls, user, obj): @@ -606,8 +600,7 @@ class Credential(CommonModelNameNotUnique): return Team.can_user_administrate(user, team_obj, data) def get_absolute_url(self): - import lib.urls - return reverse(lib.urls.views_CredentialsDetail, args=(self.pk,)) + return reverse('main:credentials_detail', args=(self.pk,)) class Team(CommonModel): ''' @@ -622,8 +615,7 @@ class Team(CommonModel): organization = models.ForeignKey('Organization', blank=False, null=True, on_delete=SET_NULL, related_name='teams') def get_absolute_url(self): - import lib.urls - return reverse(lib.urls.views_TeamsDetail, args=(self.pk,)) + return reverse('main:teams_detail', args=(self.pk,)) @classmethod def can_user_administrate(cls, user, obj, data): @@ -678,8 +670,7 @@ class Project(CommonModel): #default_playbook = models.CharField(max_length=1024) def get_absolute_url(self): - import lib.urls - return reverse(lib.urls.views_ProjectsDetail, args=(self.pk,)) + return reverse('main:projects_detail', args=(self.pk,)) @classmethod def can_user_administrate(cls, user, obj, data): @@ -777,8 +768,7 @@ class Permission(CommonModelNameNotUnique): )) def get_absolute_url(self): - import lib.urls - return reverse(lib.urls.views_PermissionsDetail, args=(self.pk,)) + return reverse('main:permissions_detail', args=(self.pk,)) @classmethod def can_user_administrate(cls, user, obj, data): @@ -892,8 +882,7 @@ class JobTemplate(CommonModel): return job def get_absolute_url(self): - import lib.urls - return reverse(lib.urls.views_JobTemplateDetail, args=(self.pk,)) + return reverse('main:job_templates_detail', args=(self.pk,)) @classmethod def can_user_read(cls, user, obj): @@ -1079,6 +1068,9 @@ class Job(CommonModel): through='JobHostSummary', ) + def get_absolute_url(self): + return reverse('main:jobs_detail', args=(self.pk,)) + @property def celery_task(self): try: diff --git a/lib/main/serializers.py b/lib/main/serializers.py index 0c52fb440c..5fe707858a 100644 --- a/lib/main/serializers.py +++ b/lib/main/serializers.py @@ -19,7 +19,6 @@ from lib.main.models import * from rest_framework import serializers, pagination from django.core.urlresolvers import reverse from django.core.serializers import json -import lib.urls class BaseSerializer(serializers.ModelSerializer): pass @@ -42,15 +41,15 @@ class OrganizationSerializer(BaseSerializer): ''' related resource URLs ''' res = dict( - audit_trail = reverse(lib.urls.views_OrganizationsAuditTrailList, args=(obj.pk,)), - projects = reverse(lib.urls.views_OrganizationsProjectsList, args=(obj.pk,)), - users = reverse(lib.urls.views_OrganizationsUsersList, args=(obj.pk,)), - admins = reverse(lib.urls.views_OrganizationsAdminsList, args=(obj.pk,)), - tags = reverse(lib.urls.views_OrganizationsTagsList, args=(obj.pk,)), - teams = reverse(lib.urls.views_OrganizationsTeamsList, args=(obj.pk,)), + audit_trail = reverse('main:organizations_audit_trail_list', args=(obj.pk,)), + projects = reverse('main:organizations_projects_list', args=(obj.pk,)), + users = reverse('main:organizations_users_list', args=(obj.pk,)), + admins = reverse('main:organizations_admins_list', args=(obj.pk,)), + tags = reverse('main:organizations_tags_list', args=(obj.pk,)), + teams = reverse('main:organizations_teams_list', args=(obj.pk,)), ) if obj.created_by: - res['created_by'] = reverse(lib.urls.views_UsersDetail, args=(obj.created_by.pk,)) + res['created_by'] = reverse('main:users_detail', args=(obj.created_by.pk,)) return res @@ -67,7 +66,7 @@ class AuditTrailSerializer(BaseSerializer): def get_related(self, obj): res = dict() if obj.modified_by: - res['modified_by'] = reverse(lib.urls.views_UsersDetail, args=(obj.modified_by.pk,)) + res['modified_by'] = reverse('main:users_detail', args=(obj.modified_by.pk,)) return res class ProjectSerializer(BaseSerializer): @@ -82,10 +81,10 @@ class ProjectSerializer(BaseSerializer): def get_related(self, obj): res = dict( - organizations = reverse(lib.urls.views_ProjectsOrganizationsList, args=(obj.pk,)), + organizations = reverse('main:projects_organizations_list', args=(obj.pk,)), ) if obj.created_by: - res['created_by'] = reverse(lib.urls.views_UsersDetail, args=(obj.created_by.pk,)) + res['created_by'] = reverse('main:users_detail', args=(obj.created_by.pk,)) return res @@ -101,12 +100,12 @@ class InventorySerializer(BaseSerializer): def get_related(self, obj): res = dict( - hosts = reverse(lib.urls.views_HostsList, args=(obj.pk,)), - groups = reverse(lib.urls.views_GroupsList, args=(obj.pk,)), - organization = reverse(lib.urls.views_OrganizationsDetail, args=(obj.organization.pk,)), + hosts = reverse('main:hosts_list', args=(obj.pk,)), + groups = reverse('main:groups_list', args=(obj.pk,)), + organization = reverse('main:organizations_detail', args=(obj.organization.pk,)), ) if obj.created_by: - res['created_by'] = reverse(lib.urls.views_UsersDetail, args=(obj.created_by.pk,)) + res['created_by'] = reverse('main:users_detail', args=(obj.created_by.pk,)) return res class HostSerializer(BaseSerializer): @@ -121,12 +120,12 @@ class HostSerializer(BaseSerializer): def get_related(self, obj): res = dict( - variable_data = reverse(lib.urls.views_HostsVariableDetail, args=(obj.pk,)), - inventory = reverse(lib.urls.views_InventoryDetail, args=(obj.inventory.pk,)), + variable_data = reverse('main:hosts_variable_detail', args=(obj.pk,)), + inventory = reverse('main:inventory_detail', args=(obj.inventory.pk,)), ) # NICE TO HAVE: possible reverse resource to show what groups the host is in if obj.created_by: - res['created_by'] = reverse(lib.urls.views_UsersDetail, args=(obj.created_by.pk,)) + res['created_by'] = reverse('main:users_detail', args=(obj.created_by.pk,)) return res class GroupSerializer(BaseSerializer): @@ -141,14 +140,14 @@ class GroupSerializer(BaseSerializer): def get_related(self, obj): res = dict( - variable_data = reverse(lib.urls.views_GroupsVariableDetail, args=(obj.pk,)), - hosts = reverse(lib.urls.views_GroupsHostsList, args=(obj.pk,)), - children = reverse(lib.urls.views_GroupsChildrenList, args=(obj.pk,)), - all_hosts = reverse(lib.urls.views_GroupsAllHostsList, args=(obj.pk,)), - inventory = reverse(lib.urls.views_InventoryDetail, args=(obj.inventory.pk,)), + variable_data = reverse('main:groups_variable_detail', args=(obj.pk,)), + hosts = reverse('main:groups_hosts_list', args=(obj.pk,)), + children = reverse('main:groups_children_list', args=(obj.pk,)), + all_hosts = reverse('main:groups_all_hosts_list', args=(obj.pk,)), + inventory = reverse('main:inventory_detail', args=(obj.inventory.pk,)), ) if obj.created_by: - res['created_by'] = reverse(lib.urls.views_UsersDetail, args=(obj.created_by.pk,)) + res['created_by'] = reverse('main:users_detail', args=(obj.created_by.pk,)) return res class TeamSerializer(BaseSerializer): @@ -165,14 +164,14 @@ class TeamSerializer(BaseSerializer): def get_related(self, obj): res = dict( - projects = reverse(lib.urls.views_TeamsProjectsList, args=(obj.pk,)), - users = reverse(lib.urls.views_TeamsUsersList, args=(obj.pk,)), - credentials = reverse(lib.urls.views_TeamsCredentialsList, args=(obj.pk,)), - organization = reverse(lib.urls.views_OrganizationsDetail, args=(obj.organization.pk,)), - permissions = reverse(lib.urls.views_TeamsPermissionsList, args=(obj.pk,)), + projects = reverse('main:teams_projects_list', args=(obj.pk,)), + users = reverse('main:teams_users_list', args=(obj.pk,)), + credentials = reverse('main:teams_credentials_list', args=(obj.pk,)), + organization = reverse('main:organizations_detail', args=(obj.organization.pk,)), + permissions = reverse('main:teams_permissions_list', args=(obj.pk,)), ) if obj.created_by: - res['created_by'] = reverse(lib.urls.views_UsersDetail, args=(obj.created_by.pk,)) + res['created_by'] = reverse('main:users_detail', args=(obj.created_by.pk,)) return res class PermissionSerializer(BaseSerializer): @@ -188,15 +187,15 @@ class PermissionSerializer(BaseSerializer): def get_related(self, obj): res = dict() if obj.user: - res['user'] = reverse(lib.urls.views_UsersDetail, args=(obj.user.pk,)) + res['user'] = reverse('main:users_detail', args=(obj.user.pk,)) if obj.team: - res['team'] = reverse(lib.urls.views_TeamsDetail, args=(obj.team.pk,)) + res['team'] = reverse('main:teams_detail', args=(obj.team.pk,)) if self.project: - res['project'] = reverse(lib.urls.views_ProjectsDetail, args=(obj.project.pk,)) + res['project'] = reverse('main:projects_detail', args=(obj.project.pk,)) if self.inventory: - res['inventory'] = reverse(lib.urls.views_InventoryDetail, args=(obj.inventory.pk,)) + res['inventory'] = reverse('main:inventory_detail', args=(obj.inventory.pk,)) if self.created_by: - res['created_by'] = reverse(lib.urls.views_UsersDetail, args=(obj.created_by.pk,)) + res['created_by'] = reverse('main:users_detail', args=(obj.created_by.pk,)) class CredentialSerializer(BaseSerializer): @@ -218,11 +217,11 @@ class CredentialSerializer(BaseSerializer): res = dict( ) if obj.user: - res['user'] = reverse(lib.urls.views_UsersDetail, args=(obj.user.pk,)) + res['user'] = reverse('main:users_detail', args=(obj.user.pk,)) if obj.team: - res['team'] = reverse(lib.urls.views_TeamsDetail, args=(obj.team.pk,)) + res['team'] = reverse('main:teams_detail', args=(obj.team.pk,)) if obj.created_by: - res['created_by'] = reverse(lib.urls.views_UsersDetail, args=(obj.created_by.pk,)) + res['created_by'] = reverse('main:users_detail', args=(obj.created_by.pk,)) return res def validate(self, attrs): @@ -247,17 +246,16 @@ class UserSerializer(BaseSerializer): def get_related(self, obj): return dict( - teams = reverse(lib.urls.views_UsersTeamsList, args=(obj.pk,)), - organizations = reverse(lib.urls.views_UsersOrganizationsList, args=(obj.pk,)), - admin_of_organizations = reverse(lib.urls.views_UsersAdminOrganizationsList, args=(obj.pk,)), - projects = reverse(lib.urls.views_UsersProjectsList, args=(obj.pk,)), - credentials = reverse(lib.urls.views_UsersCredentialsList, args=(obj.pk,)), - permissions = reverse(lib.urls.views_UsersPermissionsList, args=(obj.pk,)), + teams = reverse('main:users_teams_list', args=(obj.pk,)), + organizations = reverse('main:users_organizations_list', args=(obj.pk,)), + admin_of_organizations = reverse('main:users_admin_organizations_list', args=(obj.pk,)), + projects = reverse('main:users_projects_list', args=(obj.pk,)), + credentials = reverse('main:users_credentials_list', args=(obj.pk,)), + permissions = reverse('main:users_permissions_list', args=(obj.pk,)), ) def get_absolute_url_override(self, obj): - import lib.urls - return reverse(lib.urls.views_UsersDetail, args=(obj.pk,)) + return reverse('main:users_detail', args=(obj.pk,)) class TagSerializer(BaseSerializer): @@ -289,7 +287,7 @@ class VariableDataSerializer(BaseSerializer): res = dict( ) if obj.created_by: - res['created_by'] = reverse(lib.urls.views_UsersDetail, args=(obj.created_by.pk,)) + res['created_by'] = reverse('main:users_detail', args=(obj.created_by.pk,)) return res class JobTemplateSerializer(BaseSerializer): @@ -307,7 +305,25 @@ class JobTemplateSerializer(BaseSerializer): res = dict( ) if obj.created_by: - res['created_by'] = reverse(lib.urls.views_UsersDetail, args=(obj.created_by.pk,)) + res['created_by'] = reverse('main:users_detail', args=(obj.created_by.pk,)) + return res + +class JobSerializer(BaseSerializer): + + # add the URL and related resources + url = serializers.CharField(source='get_absolute_url', read_only=True) + related = serializers.SerializerMethodField('get_related') + + class Meta: + model = Job + fields = ('url', 'id', 'related', 'name', 'description', 'job_type', 'credential', 'project', 'inventory', 'created_by', 'creation_date') + + def get_related(self, obj): + # FIXME: fill in once further defined. related resources, credential, project, inventory, etc + res = dict( + ) + if obj.created_by: + res['created_by'] = reverse('main:users_detail', args=(obj.created_by.pk,)) return res diff --git a/lib/main/urls.py b/lib/main/urls.py new file mode 100644 index 0000000000..8363d188de --- /dev/null +++ b/lib/main/urls.py @@ -0,0 +1,157 @@ +# Copyright (c) 2013 AnsibleWorks, Inc. +# +# This file is part of Ansible Commander. +# +# Ansible Commander is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# Ansible Commander is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible Commander. If not, see . + + +from django.conf.urls import include, patterns, url as original_url +import lib.main.views as views + +def url(regex, view, kwargs=None, name=None, prefix=''): + # Set default name from view name (if a string). + if isinstance(view, basestring) and name is None: + name = view + return original_url(regex, view, kwargs, name, prefix) + +organizations_urls = patterns('lib.main.views', + url(r'^$', 'organizations_list'), + url(r'^(?P[0-9]+)/$', 'organizations_detail'), + url(r'^(?P[0-9]+)/audit_trail/$', 'organizations_audit_trail_list'), + url(r'^(?P[0-9]+)/users/$', 'organizations_users_list'), + url(r'^(?P[0-9]+)/admins/$', 'organizations_admins_list'), + url(r'^(?P[0-9]+)/projects/$', 'organizations_projects_list'), + url(r'^(?P[0-9]+)/tags/$', 'organizations_tags_list'), + url(r'^(?P[0-9]+)/teams/$', 'organizations_teams_list'), +) + +users_urls = patterns('lib.main.views', + url(r'^$', 'users_list'), + url(r'^(?P[0-9]+)/$', 'users_detail'), + url(r'^(?P[0-9]+)/teams/$', 'users_teams_list'), + url(r'^(?P[0-9]+)/organizations/$', 'users_organizations_list'), + url(r'^(?P[0-9]+)/admin_of_organizations/$', 'users_admin_organizations_list'), + url(r'^(?P[0-9]+)/projects/$', 'users_projects_list'), + url(r'^(?P[0-9]+)/credentials/$', 'users_credentials_list'), + url(r'^(?P[0-9]+)/permissions/$', 'users_permissions_list'), +) + +projects_urls = patterns('lib.main.views', + url(r'^$', 'projects_list'), + url(r'^(?P[0-9]+)/$', 'projects_detail'), + url(r'^(?P[0-9]+)/organizations/$', 'projects_organizations_list'), +) + +audit_trails_urls = patterns('lib.main.views', + #url(r'^$', 'audit_trails_list'), + #url(r'^(?P[0-9]+)/$', 'audit_trails_detail'), + # ... and ./audit_trails/ on all resources +) + +teams_urls = patterns('lib.main.views', + url(r'^$', 'teams_list'), + url(r'^(?P[0-9]+)/$', 'teams_detail'), + url(r'^(?P[0-9]+)/projects/$', 'teams_projects_list'), + url(r'^(?P[0-9]+)/users/$', 'teams_users_list'), + url(r'^(?P[0-9]+)/credentials/$', 'teams_credentials_list'), + url(r'^(?P[0-9]+)/permissions/$', 'teams_permissions_list'), +) + +inventory_urls = patterns('lib.main.views', + url(r'^$', 'inventory_list'), + url(r'^(?P[0-9]+)/$', 'inventory_detail'), + url(r'^(?P[0-9]+)/hosts/$', 'inventory_hosts_list'), + url(r'^(?P[0-9]+)/groups/$', 'inventory_groups_list'), +) + +hosts_urls = patterns('lib.main.views', + url(r'^$', 'hosts_list'), + url(r'^(?P[0-9]+)/$', 'hosts_detail'), + url(r'^(?P[0-9]+)/variable_data/$', 'hosts_variable_detail'), + url(r'^(?P[0-9]+)/job_events/', 'hosts_job_events_list'), +) + +groups_urls = patterns('lib.main.views', + url(r'^$', 'groups_list'), + url(r'^(?P[0-9]+)/$', 'groups_detail'), + url(r'^(?P[0-9]+)/children/$', 'groups_children_list'), + url(r'^(?P[0-9]+)/hosts/$', 'groups_hosts_list'), + url(r'^(?P[0-9]+)/all_hosts/$', 'groups_all_hosts_list'), + url(r'^(?P[0-9]+)/variable_data/$', 'groups_variable_detail'), +) + +variable_data_urls = patterns('lib.main.views', + url(r'^(?P[0-9]+)/$', 'variable_detail'), + # See also variable_data resources on hosts/groups. +) + +credentials_urls = patterns('lib.main.views', + url(r'^(?P[0-9]+)/$', 'credentials_detail'), + # See also credentials resources on users/teams. +) + +permissions_urls = patterns('lib.main.views', + url(r'^(?P[0-9]+)/$', 'permissions_detail'), +) + +job_templates_urls = patterns('lib.main.views', + url(r'^$', 'job_templates_list'), + url(r'^(?P[0-9]+)/$', 'job_templates_detail'), + url(r'^(?P[0-9]+)/start$', 'job_templates_start'), +) + +jobs_urls = patterns('lib.main.views', + url(r'^$', 'jobs_list'), + url(r'^(?P[0-9]+)/$', 'jobs_detail'), + url(r'^(?P[0-9]+)/hosts$', 'jobs_hosts_list'), + url(r'^(?P[0-9]+)/successful_hosts$', 'jobs_successful_hosts_list'), + url(r'^(?P[0-9]+)/changed_hosts$', 'jobs_changed_hosts_list'), + url(r'^(?P[0-9]+)/failed_hosts$', 'jobs_failed_hosts_list'), + url(r'^(?P[0-9]+)/unreachable_hosts$', 'jobs_unreachable_hosts_list'), +) + +job_events_urls = patterns('lib.main.views', + url(r'^$', 'job_events_list'), + url(r'^(?P[0-9]+)/$', 'job_events_detail'), +) + +tags_urls = patterns('lib.main.views', + url(r'^(?P[0-9]+)/$', 'tags_detail'), + # ... and tag relations on all resources +) + +v1_urls = patterns('lib.main.views', + url(r'^$', 'api_v1_root_view'), + url(r'^authtoken/$', 'auth_token_view'), + url(r'^me/$', 'users_me_list'), + url(r'^organizations/', include(organizations_urls)), + url(r'^users/', include(users_urls)), + url(r'^projects/', include(projects_urls)), + url(r'^audit_trails/', include(audit_trails_urls)), + url(r'^teams/', include(teams_urls)), + url(r'^inventories/', include(inventory_urls)), + url(r'^hosts/', include(hosts_urls)), + url(r'^groups/', include(groups_urls)), + url(r'^variable_data/', include(variable_data_urls)), + url(r'^credentials/', include(credentials_urls)), + url(r'^permissions/', include(permissions_urls)), + url(r'^job_templates/', include(job_templates_urls)), + url(r'^jobs/', include(jobs_urls)), + url(r'^job_events/', include(job_events_urls)), + url(r'^tags/', include(tags_urls)), +) + +urlpatterns = patterns('lib.main.views', + url(r'^$', 'api_root_view'), + url(r'^v1/', include(v1_urls)), +) diff --git a/lib/main/views.py b/lib/main/views.py index 4a45bee704..cbe6beb076 100644 --- a/lib/main/views.py +++ b/lib/main/views.py @@ -32,16 +32,23 @@ from rest_framework.authtoken.views import ObtainAuthToken from rest_framework.views import APIView import exceptions import datetime +import re +import sys import json as python_json from base_views import * - class ApiRootView(APIView): + ''' + Ansible Commander REST API + ''' + + def get_name(self): + return 'REST API' def get(self, request, format=None): ''' list supported API versions ''' - current = reverse(lib.urls.views_ApiV1RootView, args=[]) + current = reverse('main:api_v1_root_view', args=[]) data = dict( description = 'Ansible Commander REST API', current_version = current, @@ -52,24 +59,35 @@ class ApiRootView(APIView): return Response(data) class ApiV1RootView(APIView): + ''' + Version 1 of the REST API. + ''' + + def get_name(self): + return 'Version 1' def get(self, request, format=None): ''' list top level resources ''' data = dict( - organizations = reverse(lib.urls.views_OrganizationsList, args=[]), - users = reverse(lib.urls.views_UsersList, args=[]), - projects = reverse(lib.urls.views_ProjectsList, args=[]), - teams = reverse(lib.urls.views_TeamsList, args=[]), - inventory = reverse(lib.urls.views_InventoryList, args=[]), - groups = reverse(lib.urls.views_GroupsList, args=[]), - hosts = reverse(lib.urls.views_HostsList, args=[]), - job_templates = reverse(lib.urls.views_JobTemplatesList, args=[]), - jobs = reverse(lib.urls.views_JobsList, args=[]), + organizations = reverse('main:organizations_list'), + users = reverse('main:users_list'), + projects = reverse('main:projects_list'), + teams = reverse('main:teams_list'), + inventory = reverse('main:inventory_list'), + groups = reverse('main:groups_list'), + hosts = reverse('main:hosts_list'), + job_templates = reverse('main:job_templates_list'), + jobs = reverse('main:jobs_list'), + authtoken = reverse('main:auth_token_view'), + me = reverse('main:users_me_list'), ) return Response(data) class AuthTokenView(ObtainAuthToken): + ''' + POST username and password to obtain an auth token for subsequent requests. + ''' renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES # FIXME: Show a better form for HTML view @@ -420,6 +438,9 @@ class UsersMeList(BaseList): permission_classes = (CustomRbac,) filter_fields = ('username',) + def get_name(self): + return 'Me!' + def post(self, request, *args, **kwargs): raise PermissionDenied() @@ -658,7 +679,7 @@ class GroupsList(BaseList): or an organization admin of an inventory they are in or when I have allowing read permissions via a user or team on an inventory they are in ''' - base = Groups.objects + base = Group.objects if self.request.user.is_superuser: return base.all() admin_of = base.filter(inventory__organization__admins__in = [ self.request.user ]).distinct() @@ -861,18 +882,26 @@ class JobTemplatesList(BaseList): ).distinct() -class JobTemplateDetail(BaseDetail): +class JobTemplatesDetail(BaseDetail): model = JobTemplate serializer_class = JobTemplateSerializer permission_classes = (CustomRbac,) + def _get_queryset(self): + return self.model.objects.all() # FIXME -class JobTemplateStart(BaseDetail): +class JobTemplatesStart(BaseDetail): pass class JobsList(BaseList): - pass + + model = Job + serializer_class = JobSerializer + permission_classes = (CustomRbac,) + + def _get_queryset(self): + return self.model.objects.all() # FIXME class JobsDetail(BaseDetail): pass @@ -892,12 +921,23 @@ class JobsFailedHostsList(BaseSubList): class JobsUnreachableHostsList(BaseSubList): pass -class JobsEventsList(BaseList): +class JobEventsList(BaseList): pass -class JobsEventsDetail(BaseDetail): +class JobEventsDetail(BaseDetail): pass -class HostJobEventsList(BaseSubList): +class HostsJobEventsList(BaseSubList): pass + +# Create view functions for all of the class-based views to simplify inclusion +# in URL patterns and reverse URL lookups, converting CamelCase names to +# lowercase_with_underscore (e.g. MyView.as_view() becomes my_view). +this_module = sys.modules[__name__] +camelcase_to_underscore = lambda str: re.sub(r'(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', '_\\1', str).lower().strip('_') +for attr, value in locals().items(): + if isinstance(value, type) and issubclass(value, APIView): + name = camelcase_to_underscore(attr) + view = value.as_view() + setattr(this_module, name, view) diff --git a/lib/templates/rest_framework/api.html b/lib/templates/rest_framework/api.html index 36e7e32fb7..39ec9a2b3f 100644 --- a/lib/templates/rest_framework/api.html +++ b/lib/templates/rest_framework/api.html @@ -45,6 +45,9 @@ html body .str, html body .atv { color: #1778c3; } +html body .str a { + text-decoration: underline; +} {% endblock %} diff --git a/lib/urls.py b/lib/urls.py index d819eafde6..5595b958b6 100644 --- a/lib/urls.py +++ b/lib/urls.py @@ -16,197 +16,9 @@ from django.conf import settings from django.conf.urls import * -import lib.main.views as views - -views_ApiRootView = views.ApiRootView.as_view() -views_ApiV1RootView = views.ApiV1RootView.as_view() - -# auth token -views_AuthTokenView = views.AuthTokenView.as_view() - -# organizations service -views_OrganizationsList = views.OrganizationsList.as_view() -views_OrganizationsDetail = views.OrganizationsDetail.as_view() -views_OrganizationsAuditTrailList = views.OrganizationsAuditTrailList.as_view() -views_OrganizationsUsersList = views.OrganizationsUsersList.as_view() -views_OrganizationsAdminsList = views.OrganizationsAdminsList.as_view() -views_OrganizationsProjectsList = views.OrganizationsProjectsList.as_view() -views_OrganizationsTagsList = views.OrganizationsTagsList.as_view() -views_OrganizationsTeamsList = views.OrganizationsTeamsList.as_view() - -# users service -views_UsersList = views.UsersList.as_view() -views_UsersDetail = views.UsersDetail.as_view() -views_UsersMeList = views.UsersMeList.as_view() -views_UsersTeamsList = views.UsersTeamsList.as_view() -views_UsersOrganizationsList = views.UsersOrganizationsList.as_view() -views_UsersAdminOrganizationsList = views.UsersAdminOrganizationsList.as_view() -views_UsersProjectsList = views.UsersProjectsList.as_view() -views_UsersCredentialsList = views.UsersCredentialsList.as_view() - -# projects service -views_ProjectsList = views.ProjectsList.as_view() -views_ProjectsDetail = views.ProjectsDetail.as_view() -views_ProjectsOrganizationsList = views.ProjectsOrganizationsList.as_view() - -# audit trail service - -# team service -views_TeamsList = views.TeamsList.as_view() -views_TeamsDetail = views.TeamsDetail.as_view() -views_TeamsUsersList = views.TeamsUsersList.as_view() -views_TeamsCredentialsList = views.TeamsCredentialsList.as_view() -views_TeamsCredentialsList = views.TeamsCredentialsList.as_view() -views_TeamsProjectsList = views.TeamsProjectsList.as_view() - -# inventory service -views_InventoryList = views.InventoryList.as_view() -views_InventoryDetail = views.InventoryDetail.as_view() -views_InventoryHostsList = views.InventoryHostsList.as_view() -views_InventoryGroupsList = views.InventoryGroupsList.as_view() - -# group service -views_GroupsList = views.GroupsList.as_view() -views_GroupsDetail = views.GroupsDetail.as_view() -views_GroupsVariableDetail = views.GroupsVariableDetail.as_view() -views_GroupsChildrenList = views.GroupsChildrenList.as_view() -views_GroupsAllHostsList = views.GroupsAllHostsList.as_view() -views_GroupsHostsList = views.GroupsHostsList.as_view() - -# host service -views_HostsList = views.HostsList.as_view() -views_HostsDetail = views.HostsDetail.as_view() -views_HostsVariableDetail = views.HostsVariableDetail.as_view() - -# seperate variable data -views_VariableDetail = views.VariableDetail.as_view() - -# jobs services -views_JobTemplatesList = views.JobTemplatesList.as_view() -views_JobTemplateDetail = views.JobTemplateDetail.as_view() -views_JobTemplateStart = views.JobTemplateStart.as_view() -views_JobsList = views.JobsList.as_view() -views_JobsDetail = views.JobsDetail.as_view() -views_JobsHostsList = views.JobsHostsList.as_view() -views_JobsSuccessfulHostsList = views.JobsSuccessfulHostsList.as_view() -views_JobsChangedHostsList = views.JobsChangedHostsList.as_view() -views_JobsFailedHostsList = views.JobsFailedHostsList.as_view() -views_JobsUnreachableHostsList = views.JobsUnreachableHostsList.as_view() -views_JobsEventsList = views.JobsEventsList.as_view() -views_JobsEventsDetail = views.JobsEventsDetail.as_view() -views_HostJobEventsList = views.HostJobEventsList.as_view() - -# tags service -views_TagsDetail = views.TagsDetail.as_view() - -# credentials service -views_CredentialsDetail = views.CredentialsDetail.as_view() - -# permissions -views_UsersPermissionsList = views.UsersPermissionsList.as_view() -views_TeamsPermissionsList = views.TeamsPermissionsList.as_view() -views_PermissionsDetail = views.PermissionsDetail.as_view() urlpatterns = patterns('', - - url(r'^api/$', views_ApiRootView), - url(r'^api/v1/$', views_ApiV1RootView), - - # obtain auth token - url(r'^api/v1/authtoken/$', views_AuthTokenView), - - # organizations vice - url(r'^api/v1/organizations/$', views_OrganizationsList), - url(r'^api/v1/organizations/(?P[0-9]+)/$', views_OrganizationsDetail), - url(r'^api/v1/organizations/(?P[0-9]+)/audit_trail/$', views_OrganizationsAuditTrailList), - url(r'^api/v1/organizations/(?P[0-9]+)/users/$', views_OrganizationsUsersList), - url(r'^api/v1/organizations/(?P[0-9]+)/admins/$', views_OrganizationsAdminsList), - url(r'^api/v1/organizations/(?P[0-9]+)/projects/$', views_OrganizationsProjectsList), - url(r'^api/v1/organizations/(?P[0-9]+)/tags/$', views_OrganizationsTagsList), - url(r'^api/v1/organizations/(?P[0-9]+)/teams/$', views_OrganizationsTeamsList), - - # users service - url(r'^api/v1/me/$', views_UsersMeList), - url(r'^api/v1/users/$', views_UsersList), - url(r'^api/v1/users/(?P[0-9]+)/$', views_UsersDetail), - url(r'^api/v1/users/(?P[0-9]+)/teams/$', views_UsersTeamsList), - url(r'^api/v1/users/(?P[0-9]+)/organizations/$', views_UsersOrganizationsList), - url(r'^api/v1/users/(?P[0-9]+)/admin_of_organizations/$', views_UsersAdminOrganizationsList), - url(r'^api/v1/users/(?P[0-9]+)/projects/$', views_UsersProjectsList), - url(r'^api/v1/users/(?P[0-9]+)/credentials/$', views_UsersCredentialsList), - - # projects service - url(r'^api/v1/projects/$', views_ProjectsList), - url(r'^api/v1/projects/(?P[0-9]+)/$', views_ProjectsDetail), - url(r'^api/v1/projects/(?P[0-9]+)/organizations/$', views_ProjectsOrganizationsList), - - # audit trail service - # api/v1/audit_trails/ - # api/v1/audit_trails/N/ - # and ./audit_trails/ on all resources - - # team service - # api/v1/teams/ - url(r'^api/v1/teams/$', views_TeamsList), - url(r'^api/v1/teams/(?P[0-9]+)/$', views_TeamsDetail), - url(r'^api/v1/teams/(?P[0-9]+)/projects/$', views_TeamsProjectsList), - url(r'^api/v1/teams/(?P[0-9]+)/users/$', views_TeamsUsersList), - url(r'^api/v1/teams/(?P[0-9]+)/credentials/$', views_TeamsCredentialsList), - - # api/v1/teams/N/ - # api/v1/teams/N/users/ - - # inventory service - url(r'^api/v1/inventories/$', views_InventoryList), - url(r'^api/v1/inventories/(?P[0-9]+)/$', views_InventoryDetail), - url(r'^api/v1/inventories/(?P[0-9]+)/hosts/$', views_InventoryHostsList), - url(r'^api/v1/inventories/(?P[0-9]+)/groups/$', views_InventoryGroupsList), - - # host service - url(r'^api/v1/hosts/$', views_HostsList), - url(r'^api/v1/hosts/(?P[0-9]+)/$', views_HostsDetail), - - # group service - url(r'^api/v1/groups/$', views_GroupsList), - url(r'^api/v1/groups/(?P[0-9]+)/$', views_GroupsDetail), - url(r'^api/v1/groups/(?P[0-9]+)/children/$', views_GroupsChildrenList), - url(r'^api/v1/groups/(?P[0-9]+)/hosts/$', views_GroupsHostsList), - url(r'^api/v1/groups/(?P[0-9]+)/all_hosts/$', views_GroupsAllHostsList), - - # variable data - url(r'^api/v1/hosts/(?P[0-9]+)/variable_data/$', views_HostsVariableDetail), - url(r'^api/v1/groups/(?P[0-9]+)/variable_data/$', views_GroupsVariableDetail), - url(r'^api/v1/variable_data/(?P[0-9]+)/$', views_VariableDetail), - - # log data (results) services - - # jobs & job status services - url(r'^api/v1/job_templates/$', views_JobTemplatesList), - url(r'^api/v1/job_templates/(?P[0-9]+)/$', views_JobTemplateDetail), - url(r'^api/v1/job_templates/(?P[0-9]+)/start$', views_JobTemplateStart), - url(r'^api/v1/jobs/$', views_JobsList), - url(r'^api/v1/jobs/(?P[0-9]+)/$', views_JobsDetail), - url(r'^api/v1/jobs/(?P[0-9]+)/hosts$', views_JobsHostsList), - url(r'^api/v1/jobs/(?P[0-9]+)/successful_hosts$', views_JobsSuccessfulHostsList), - url(r'^api/v1/jobs/(?P[0-9]+)/changed_hosts$', views_JobsChangedHostsList), - url(r'^api/v1/jobs/(?P[0-9]+)/failed_hosts$', views_JobsFailedHostsList), - url(r'^api/v1/jobs/(?P[0-9]+)/unreachable_hosts$', views_JobsUnreachableHostsList), - url(r'^api/v1/job_events/$', views_JobsEventsList), - url(r'^api/v1/job_events/(?P[0-9]+)/$', views_JobsEventsDetail), - url(r'^api/v1/hosts/(?P[0-9]+)/job_events/', views_HostJobEventsList), - - # tags service - url(r'^api/v1/tags/(?P[0-9]+)/$', views_TagsDetail), - # ... and tag relations on all resources - - # credentials services - url(r'^api/v1/credentials/(?P[0-9]+)/$', views_CredentialsDetail), - - # permissions services - url(r'^api/v1/users/(?P[0-9]+)/permissions/$', views_UsersPermissionsList), - url(r'^api/v1/teams/(?P[0-9]+)/permissions/$', views_TeamsPermissionsList), - url(r'^api/v1/permissions/(?P[0-9]+)/$', views_PermissionsDetail), - + url(r'^api/', include('lib.main.urls', namespace='main', app_name='main')), ) if 'django.contrib.admin' in settings.INSTALLED_APPS: