diff --git a/ansibleworks/main/base_views.py b/ansibleworks/main/base_views.py index aedb311a18..621f37aa85 100644 --- a/ansibleworks/main/base_views.py +++ b/ansibleworks/main/base_views.py @@ -142,9 +142,9 @@ class BaseSubList(BaseList): if self.__class__.parent_model == Organization: organization = Organization.objects.get(pk=data[inject_primary_key]) import ansibleworks.main.views - if self.__class__ == ansibleworks.main.views.OrganizationsUsersList: + if self.__class__ == ansibleworks.main.views.OrganizationUsersList: organization.users.add(obj) - elif self.__class__ == ansibleworks.main.views.OrganizationsAdminsList: + elif self.__class__ == ansibleworks.main.views.OrganizationAdminsList: organization.admins.add(obj) else: diff --git a/ansibleworks/main/models/__init__.py b/ansibleworks/main/models/__init__.py index 7b7482fd08..b38c85556c 100644 --- a/ansibleworks/main/models/__init__.py +++ b/ansibleworks/main/models/__init__.py @@ -155,7 +155,7 @@ class Organization(CommonModel): projects = models.ManyToManyField('Project', blank=True, related_name='organizations') def get_absolute_url(self): - return reverse('main:organizations_detail', args=(self.pk,)) + return reverse('main:organization_detail', args=(self.pk,)) def __unicode__(self): return self.name @@ -206,7 +206,7 @@ class Host(CommonModelNameNotUnique): return self.name def get_absolute_url(self): - return reverse('main:hosts_detail', args=(self.pk,)) + return reverse('main:host_detail', args=(self.pk,)) def update_has_active_failures(self, update_groups=True, update_inventory=True): @@ -259,7 +259,7 @@ class Group(CommonModelNameNotUnique): return self.name def get_absolute_url(self): - return reverse('main:groups_detail', args=(self.pk,)) + return reverse('main:group_detail', args=(self.pk,)) def update_has_active_failures(self): failed_hosts = self.all_hosts.filter(active=True, @@ -398,7 +398,7 @@ class Credential(CommonModelNameNotUnique): return self.sudo_password == 'ASK' def get_absolute_url(self): - return reverse('main:credentials_detail', args=(self.pk,)) + return reverse('main:credential_detail', args=(self.pk,)) class Team(CommonModel): ''' @@ -413,7 +413,7 @@ class Team(CommonModel): organization = models.ForeignKey('Organization', blank=False, null=True, on_delete=SET_NULL, related_name='teams') def get_absolute_url(self): - return reverse('main:teams_detail', args=(self.pk,)) + return reverse('main:team_detail', args=(self.pk,)) class Project(CommonModel): ''' @@ -449,7 +449,7 @@ class Project(CommonModel): #default_playbook = models.CharField(max_length=1024) def get_absolute_url(self): - return reverse('main:projects_detail', args=(self.pk,)) + return reverse('main:project_detail', args=(self.pk,)) def get_project_path(self): local_path = os.path.basename(self.local_path) @@ -531,7 +531,7 @@ class Permission(CommonModelNameNotUnique): )) def get_absolute_url(self): - return reverse('main:permissions_detail', args=(self.pk,)) + return reverse('main:permission_detail', args=(self.pk,)) # TODO: other job types (later) diff --git a/ansibleworks/main/serializers.py b/ansibleworks/main/serializers.py index 2b91a2d30c..19589e3e81 100644 --- a/ansibleworks/main/serializers.py +++ b/ansibleworks/main/serializers.py @@ -15,17 +15,17 @@ from rest_framework import serializers # AnsibleWorks from ansibleworks.main.models import * -BASE_FIELDS = ('id', 'url', 'related', 'summary_fields', 'created', - 'creation_date', 'name', 'description') +BASE_FIELDS = ('id', 'url', 'related', 'summary_fields', 'created', 'name', + 'description') # objects that if found we should add summary info for them SUMMARIZABLE_FKS = ( - 'organization', 'host', 'group', 'inventory', 'project', 'team', 'job', 'job_template', - 'credential', 'permission' + 'organization', 'host', 'group', 'inventory', 'project', 'team', 'job', + 'job_template', 'credential', 'permission', ) # fields that should be summarized regardless of object type SUMMARIZABLE_FIELDS = ( - 'name', 'username', 'first_name', 'last_name', 'description' + 'name', 'username', 'first_name', 'last_name', 'description', ) class BaseSerializer(serializers.ModelSerializer): @@ -37,19 +37,18 @@ class BaseSerializer(serializers.ModelSerializer): # make certain fields read only created = serializers.SerializerMethodField('get_created') - creation_date = serializers.SerializerMethodField('get_creation_date') # FIXME: temporarily left this field in case anything uses it.. should be removed. active = serializers.SerializerMethodField('get_active') def get_absolute_url(self, obj): if isinstance(obj, User): - return reverse('main:users_detail', args=(obj.pk,)) + return reverse('main:user_detail', args=(obj.pk,)) else: return obj.get_absolute_url() def get_related(self, obj): res = dict() if getattr(obj, 'created_by', None): - res['created_by'] = reverse('main:users_detail', args=(obj.created_by.pk,)) + res['created_by'] = reverse('main:user_detail', args=(obj.created_by.pk,)) return res def get_summary_fields(self, obj): @@ -70,12 +69,6 @@ class BaseSerializer(serializers.ModelSerializer): pass return summary_fields - def get_creation_date(self, obj): - if isinstance(obj, User): - return obj.date_joined.date() - else: - return obj.created.date() - def get_created(self, obj): if isinstance(obj, User): return obj.date_joined @@ -97,13 +90,13 @@ class OrganizationSerializer(BaseSerializer): def get_related(self, obj): res = super(OrganizationSerializer, self).get_related(obj) res.update(dict( - #audit_trail = reverse('main:organizations_audit_trail_list', args=(obj.pk,)), - projects = reverse('main:organizations_projects_list', args=(obj.pk,)), - inventories = reverse('main:organizations_inventories_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,)), + #audit_trail = reverse('main:organization_audit_trail_list', args=(obj.pk,)), + projects = reverse('main:organization_projects_list', args=(obj.pk,)), + inventories = reverse('main:organization_inventories_list', args=(obj.pk,)), + users = reverse('main:organization_users_list', args=(obj.pk,)), + admins = reverse('main:organization_admins_list', args=(obj.pk,)), + #tags = reverse('main:organization_tags_list', args=(obj.pk,)), + teams = reverse('main:organization_teams_list', args=(obj.pk,)), )) return res @@ -114,13 +107,12 @@ class ProjectSerializer(BaseSerializer): class Meta: model = Project fields = BASE_FIELDS + ('local_path',) - # 'default_playbook', 'scm_type') def get_related(self, obj): res = super(ProjectSerializer, self).get_related(obj) res.update(dict( - organizations = reverse('main:projects_organizations_list', args=(obj.pk,)), - playbooks = reverse('main:projects_detail_playbooks', args=(obj.pk,)), + organizations = reverse('main:project_organizations_list', args=(obj.pk,)), + playbooks = reverse('main:project_detail_playbooks', args=(obj.pk,)), )) return res @@ -148,7 +140,7 @@ class InventorySerializer(BaseSerializer): groups = reverse('main:inventory_groups_list', args=(obj.pk,)), root_groups = reverse('main:inventory_root_groups_list', args=(obj.pk,)), variable_data = reverse('main:inventory_variable_detail', args=(obj.pk,)), - organization = reverse('main:organizations_detail', args=(obj.organization.pk,)), + organization = reverse('main:organization_detail', args=(obj.organization.pk,)), )) return res @@ -161,16 +153,17 @@ class HostSerializer(BaseSerializer): def get_related(self, obj): res = super(HostSerializer, self).get_related(obj) res.update(dict( - variable_data = reverse('main:hosts_variable_detail', args=(obj.pk,)), - inventory = reverse('main:inventory_detail', args=(obj.inventory.pk,)), - job_events = reverse('main:host_job_event_list', args=(obj.pk,)), - job_host_summaries = reverse('main:host_job_host_summary_list', args=(obj.pk,)), + variable_data = reverse('main:host_variable_detail', args=(obj.pk,)), + inventory = reverse('main:inventory_detail', args=(obj.inventory.pk,)), + groups = reverse('main:host_groups_list', args=(obj.pk,)), + all_groups = reverse('main:host_all_groups_list', args=(obj.pk,)), + job_events = reverse('main:host_job_events_list', args=(obj.pk,)), + job_host_summaries = reverse('main:host_job_host_summaries_list', args=(obj.pk,)), )) if obj.last_job: res['last_job'] = reverse('main:job_detail', args=(obj.last_job.pk,)) if obj.last_job_host_summary: res['last_job_host_summary'] = reverse('main:job_host_summary_detail', args=(obj.last_job_host_summary.pk,)) - # NICE TO HAVE: possible reverse resource to show what groups the host is in return res class GroupSerializer(BaseSerializer): @@ -182,13 +175,13 @@ class GroupSerializer(BaseSerializer): def get_related(self, obj): res = super(GroupSerializer, self).get_related(obj) res.update(dict( - 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,)), + variable_data = reverse('main:group_variable_detail', args=(obj.pk,)), + hosts = reverse('main:group_hosts_list', args=(obj.pk,)), + children = reverse('main:group_children_list', args=(obj.pk,)), + all_hosts = reverse('main:group_all_hosts_list', args=(obj.pk,)), inventory = reverse('main:inventory_detail', args=(obj.inventory.pk,)), - job_events = reverse('main:group_job_event_list', args=(obj.pk,)), - job_host_summaries = reverse('main:group_job_host_summary_list', args=(obj.pk,)), + job_events = reverse('main:group_job_events_list', args=(obj.pk,)), + job_host_summaries = reverse('main:group_job_host_summaries_list', args=(obj.pk,)), )) return res @@ -229,11 +222,11 @@ class TeamSerializer(BaseSerializer): def get_related(self, obj): res = super(TeamSerializer, self).get_related(obj) res.update(dict( - 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,)), + projects = reverse('main:team_projects_list', args=(obj.pk,)), + users = reverse('main:team_users_list', args=(obj.pk,)), + credentials = reverse('main:team_credentials_list', args=(obj.pk,)), + organization = reverse('main:organization_detail', args=(obj.organization.pk,)), + permissions = reverse('main:team_permissions_list', args=(obj.pk,)), )) return res @@ -247,11 +240,11 @@ class PermissionSerializer(BaseSerializer): def get_related(self, obj): res = super(PermissionSerializer, self).get_related(obj) if obj.user: - res['user'] = reverse('main:users_detail', args=(obj.user.pk,)) + res['user'] = reverse('main:user_detail', args=(obj.user.pk,)) if obj.team: - res['team'] = reverse('main:teams_detail', args=(obj.team.pk,)) + res['team'] = reverse('main:team_detail', args=(obj.team.pk,)) if obj.project: - res['project'] = reverse('main:projects_detail', args=(obj.project.pk,)) + res['project'] = reverse('main:project_detail', args=(obj.project.pk,)) if obj.inventory: res['inventory'] = reverse('main:inventory_detail', args=(obj.inventory.pk,)) return res @@ -269,9 +262,9 @@ class CredentialSerializer(BaseSerializer): def get_related(self, obj): res = super(CredentialSerializer, self).get_related(obj) if obj.user: - res['user'] = reverse('main:users_detail', args=(obj.user.pk,)) + res['user'] = reverse('main:user_detail', args=(obj.user.pk,)) if obj.team: - res['team'] = reverse('main:teams_detail', args=(obj.team.pk,)) + res['team'] = reverse('main:team_detail', args=(obj.team.pk,)) return res def validate(self, attrs): @@ -288,19 +281,18 @@ class UserSerializer(BaseSerializer): class Meta: model = User - fields = ('id', 'url', 'related', 'created', 'creation_date', - 'username', 'first_name', 'last_name', 'email', 'is_active', - 'is_superuser',) + fields = ('id', 'url', 'related', 'created', 'username', 'first_name', + 'last_name', 'email', 'is_active', 'is_superuser',) def get_related(self, obj): res = super(UserSerializer, self).get_related(obj) res.update(dict( - 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,)), + teams = reverse('main:user_teams_list', args=(obj.pk,)), + organizations = reverse('main:user_organizations_list', args=(obj.pk,)), + admin_of_organizations = reverse('main:user_admin_of_organizations_list', args=(obj.pk,)), + projects = reverse('main:user_projects_list', args=(obj.pk,)), + credentials = reverse('main:user_credentials_list', args=(obj.pk,)), + permissions = reverse('main:user_permissions_list', args=(obj.pk,)), )) return res @@ -316,11 +308,11 @@ class JobTemplateSerializer(BaseSerializer): res = super(JobTemplateSerializer, self).get_related(obj) res.update(dict( inventory = reverse('main:inventory_detail', args=(obj.inventory.pk,)), - project = reverse('main:projects_detail', args=(obj.project.pk,)), - jobs = reverse('main:job_template_job_list', args=(obj.pk,)), + project = reverse('main:project_detail', args=(obj.project.pk,)), + jobs = reverse('main:job_template_jobs_list', args=(obj.pk,)), )) if obj.credential: - res['credential'] = reverse('main:credentials_detail', args=(obj.credential.pk,)) + res['credential'] = reverse('main:credential_detail', args=(obj.credential.pk,)) return res def validate_playbook(self, attrs, source): @@ -347,10 +339,10 @@ class JobSerializer(BaseSerializer): res = super(JobSerializer, self).get_related(obj) res.update(dict( inventory = reverse('main:inventory_detail', args=(obj.inventory.pk,)), - project = reverse('main:projects_detail', args=(obj.project.pk,)), - credential = reverse('main:credentials_detail', args=(obj.credential.pk,)), - job_events = reverse('main:job_job_event_list', args=(obj.pk,)), - job_host_summaries = reverse('main:job_job_host_summary_list', args=(obj.pk,)), + project = reverse('main:project_detail', args=(obj.project.pk,)), + credential = reverse('main:credential_detail', args=(obj.credential.pk,)), + job_events = reverse('main:job_job_events_list', args=(obj.pk,)), + job_host_summaries = reverse('main:job_job_host_summaries_list', args=(obj.pk,)), )) if obj.job_template: res['job_template'] = reverse('main:job_template_detail', args=(obj.job_template.pk,)) @@ -395,7 +387,7 @@ class JobHostSummarySerializer(BaseSerializer): res = super(JobHostSummarySerializer, self).get_related(obj) res.update(dict( job=reverse('main:job_detail', args=(obj.job.pk,)), - host=reverse('main:hosts_detail', args=(obj.host.pk,)) + host=reverse('main:host_detail', args=(obj.host.pk,)) )) return res @@ -414,5 +406,5 @@ class JobEventSerializer(BaseSerializer): job = reverse('main:job_detail', args=(obj.job.pk,)), )) if obj.host: - res['host'] = reverse('main:hosts_detail', args=(obj.host.pk,)) + res['host'] = reverse('main:host_detail', args=(obj.host.pk,)) return res diff --git a/ansibleworks/main/tests/base.py b/ansibleworks/main/tests/base.py index 4d3580d408..523ef2139e 100644 --- a/ansibleworks/main/tests/base.py +++ b/ansibleworks/main/tests/base.py @@ -162,8 +162,8 @@ class BaseTestMixin(object): else: response = method(url) - if response.status_code == 500 and expect != 500: - assert False, "Failed: %s" % response.content + self.assertFalse(response.status_code == 500 and expect != 500, + 'Failed (500): %s' % response.content) if expect is not None: assert response.status_code == expect, "expected status %s, got %s for url=%s as auth=%s: %s" % (expect, response.status_code, url, auth, response.content) if method_name == 'head': diff --git a/ansibleworks/main/tests/jobs.py b/ansibleworks/main/tests/jobs.py index e73710793b..b76707bd20 100644 --- a/ansibleworks/main/tests/jobs.py +++ b/ansibleworks/main/tests/jobs.py @@ -558,7 +558,7 @@ class JobTemplateTest(BaseJobTestMixin, django.test.TestCase): def test_get_job_template_job_list(self): jt = self.jt_eng_run - url = reverse('main:job_template_job_list', args=(jt.pk,)) + url = reverse('main:job_template_jobs_list', args=(jt.pk,)) # Test with no auth and with invalid login. self._test_invalid_creds(url) @@ -576,7 +576,7 @@ class JobTemplateTest(BaseJobTestMixin, django.test.TestCase): def test_post_job_template_job_list(self): jt = self.jt_eng_run - url = reverse('main:job_template_job_list', args=(jt.pk,)) + url = reverse('main:job_template_jobs_list', args=(jt.pk,)) data = dict( name='new job from template', credential=self.cred_bob.pk, @@ -882,7 +882,7 @@ class JobStartCancelTest(BaseJobTestMixin, django.test.TransactionTestCase): self.assertTrue(response['result_stdout']) # Test job events for completed job. - url = reverse('main:job_job_event_list', args=(job.pk,)) + url = reverse('main:job_job_events_list', args=(job.pk,)) with self.current_user(self.user_sue): response = self.get(url) qs = job.job_events.all() @@ -901,7 +901,7 @@ class JobStartCancelTest(BaseJobTestMixin, django.test.TransactionTestCase): # Also test job event list for each host. for host in Host.objects.filter(pk__in=host_ids): - url = reverse('main:host_job_event_list', args=(host.pk,)) + url = reverse('main:host_job_events_list', args=(host.pk,)) with self.current_user(self.user_sue): response = self.get(url) qs = host.job_events.all() @@ -911,7 +911,7 @@ class JobStartCancelTest(BaseJobTestMixin, django.test.TransactionTestCase): # Test job event list for groups. for group in self.inv_ops_east.groups.all(): - url = reverse('main:group_job_event_list', args=(group.pk,)) + url = reverse('main:group_job_events_list', args=(group.pk,)) with self.current_user(self.user_sue): response = self.get(url) qs = group.job_events.all() @@ -929,7 +929,7 @@ class JobStartCancelTest(BaseJobTestMixin, django.test.TransactionTestCase): self.check_list_ids(response, qs) # Test job host summaries for completed job. - url = reverse('main:job_job_host_summary_list', args=(job.pk,)) + url = reverse('main:job_job_host_summaries_list', args=(job.pk,)) with self.current_user(self.user_sue): response = self.get(url) qs = job.job_host_summaries.all() @@ -950,7 +950,7 @@ class JobStartCancelTest(BaseJobTestMixin, django.test.TransactionTestCase): # Test job host summaries for each host. for host in Host.objects.filter(pk__in=host_ids): - url = reverse('main:host_job_host_summary_list', args=(host.pk,)) + url = reverse('main:host_job_host_summaries_list', args=(host.pk,)) with self.current_user(self.user_sue): response = self.get(url) qs = host.job_host_summaries.all() @@ -960,7 +960,7 @@ class JobStartCancelTest(BaseJobTestMixin, django.test.TransactionTestCase): # Test job host summaries for groups. for group in self.inv_ops_east.groups.all(): - url = reverse('main:group_job_host_summary_list', args=(group.pk,)) + url = reverse('main:group_job_host_summaries_list', args=(group.pk,)) with self.current_user(self.user_sue): response = self.get(url) qs = group.job_host_summaries.all() diff --git a/ansibleworks/main/tests/organizations.py b/ansibleworks/main/tests/organizations.py index 4e891f5c3c..294aa13125 100644 --- a/ansibleworks/main/tests/organizations.py +++ b/ansibleworks/main/tests/organizations.py @@ -52,7 +52,7 @@ class OrganizationsTest(BaseTest): self.organizations[1].admins.add(self.normal_django_user) def test_get_list(self): - url = reverse('main:organizations_list') + url = reverse('main:organization_list') # no credentials == 401 self.options(url, expect=401) diff --git a/ansibleworks/main/tests/projects.py b/ansibleworks/main/tests/projects.py index 31366239cc..abee4af4c8 100644 --- a/ansibleworks/main/tests/projects.py +++ b/ansibleworks/main/tests/projects.py @@ -422,7 +422,7 @@ class ProjectsTest(BaseTest): self.get(team_creds, expect=403, auth=self.get_nobody_credentials()) # Check /api/v1/credentials (GET) - url = reverse('main:credentials_list') + url = reverse('main:credential_list') with self.current_user(self.super_django_user): self.options(url) self.head(url) diff --git a/ansibleworks/main/urls.py b/ansibleworks/main/urls.py index 2b5b5636f8..a752ac4d4a 100644 --- a/ansibleworks/main/urls.py +++ b/ansibleworks/main/urls.py @@ -9,41 +9,41 @@ def url(regex, view, kwargs=None, name=None, prefix=''): name = view return original_url(regex, view, kwargs, name, prefix) -organizations_urls = patterns('ansibleworks.main.views', - url(r'^$', 'organizations_list'), - url(r'^(?P[0-9]+)/$', 'organizations_detail'), - url(r'^(?P[0-9]+)/users/$', 'organizations_users_list'), - url(r'^(?P[0-9]+)/admins/$', 'organizations_admins_list'), - url(r'^(?P[0-9]+)/inventories/$', 'organizations_inventories_list'), - url(r'^(?P[0-9]+)/projects/$', 'organizations_projects_list'), - url(r'^(?P[0-9]+)/teams/$', 'organizations_teams_list'), +organization_urls = patterns('ansibleworks.main.views', + url(r'^$', 'organization_list'), + url(r'^(?P[0-9]+)/$', 'organization_detail'), + url(r'^(?P[0-9]+)/users/$', 'organization_users_list'), + url(r'^(?P[0-9]+)/admins/$', 'organization_admins_list'), + url(r'^(?P[0-9]+)/inventories/$', 'organization_inventories_list'), + url(r'^(?P[0-9]+)/projects/$', 'organization_projects_list'), + url(r'^(?P[0-9]+)/teams/$', 'organization_teams_list'), ) -users_urls = patterns('ansibleworks.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'), +user_urls = patterns('ansibleworks.main.views', + url(r'^$', 'user_list'), + url(r'^(?P[0-9]+)/$', 'user_detail'), + url(r'^(?P[0-9]+)/teams/$', 'user_teams_list'), + url(r'^(?P[0-9]+)/organizations/$', 'user_organizations_list'), + url(r'^(?P[0-9]+)/admin_of_organizations/$', 'user_admin_of_organizations_list'), + url(r'^(?P[0-9]+)/projects/$', 'user_projects_list'), + url(r'^(?P[0-9]+)/credentials/$', 'user_credentials_list'), + url(r'^(?P[0-9]+)/permissions/$', 'user_permissions_list'), ) -projects_urls = patterns('ansibleworks.main.views', - url(r'^$', 'projects_list'), - url(r'^(?P[0-9]+)/$', 'projects_detail'), - url(r'^(?P[0-9]+)/playbooks/$', 'projects_detail_playbooks'), - url(r'^(?P[0-9]+)/organizations/$', 'projects_organizations_list'), +project_urls = patterns('ansibleworks.main.views', + url(r'^$', 'project_list'), + url(r'^(?P[0-9]+)/$', 'project_detail'), + url(r'^(?P[0-9]+)/playbooks/$', 'project_detail_playbooks'), + url(r'^(?P[0-9]+)/organizations/$', 'project_organizations_list'), ) -teams_urls = patterns('ansibleworks.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'), +team_urls = patterns('ansibleworks.main.views', + url(r'^$', 'team_list'), + url(r'^(?P[0-9]+)/$', 'team_detail'), + url(r'^(?P[0-9]+)/projects/$', 'team_projects_list'), + url(r'^(?P[0-9]+)/users/$', 'team_users_list'), + url(r'^(?P[0-9]+)/credentials/$', 'team_credentials_list'), + url(r'^(?P[0-9]+)/permissions/$', 'team_permissions_list'), ) inventory_urls = patterns('ansibleworks.main.views', @@ -55,59 +55,57 @@ inventory_urls = patterns('ansibleworks.main.views', url(r'^(?P[0-9]+)/variable_data/$', 'inventory_variable_detail'), ) -hosts_urls = patterns('ansibleworks.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/', 'host_job_event_list'), - url(r'^(?P[0-9]+)/job_host_summaries/$', 'host_job_host_summary_list'), +host_urls = patterns('ansibleworks.main.views', + url(r'^$', 'host_list'), + url(r'^(?P[0-9]+)/$', 'host_detail'), + url(r'^(?P[0-9]+)/variable_data/$', 'host_variable_detail'), + url(r'^(?P[0-9]+)/groups/$', 'host_groups_list'), + url(r'^(?P[0-9]+)/all_groups/$', 'host_all_groups_list'), + url(r'^(?P[0-9]+)/job_events/', 'host_job_events_list'), + url(r'^(?P[0-9]+)/job_host_summaries/$', 'host_job_host_summaries_list'), ) -groups_urls = patterns('ansibleworks.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'), - url(r'^(?P[0-9]+)/job_events/$', 'group_job_event_list'), - url(r'^(?P[0-9]+)/job_host_summaries/$', 'group_job_host_summary_list'), +group_urls = patterns('ansibleworks.main.views', + url(r'^$', 'group_list'), + url(r'^(?P[0-9]+)/$', 'group_detail'), + url(r'^(?P[0-9]+)/children/$', 'group_children_list'), + url(r'^(?P[0-9]+)/hosts/$', 'group_hosts_list'), + url(r'^(?P[0-9]+)/all_hosts/$', 'group_all_hosts_list'), + url(r'^(?P[0-9]+)/variable_data/$', 'group_variable_detail'), + url(r'^(?P[0-9]+)/job_events/$', 'group_job_events_list'), + url(r'^(?P[0-9]+)/job_host_summaries/$', 'group_job_host_summaries_list'), ) -credentials_urls = patterns('ansibleworks.main.views', - url(r'^$', 'credentials_list'), - url(r'^(?P[0-9]+)/$', 'credentials_detail'), +credential_urls = patterns('ansibleworks.main.views', + url(r'^$', 'credential_list'), + url(r'^(?P[0-9]+)/$', 'credential_detail'), # See also credentials resources on users/teams. ) -permissions_urls = patterns('ansibleworks.main.views', - url(r'^(?P[0-9]+)/$', 'permissions_detail'), +permission_urls = patterns('ansibleworks.main.views', + url(r'^(?P[0-9]+)/$', 'permission_detail'), ) -job_templates_urls = patterns('ansibleworks.main.views', +job_template_urls = patterns('ansibleworks.main.views', url(r'^$', 'job_template_list'), url(r'^(?P[0-9]+)/$', 'job_template_detail'), - url(r'^(?P[0-9]+)/jobs/$', 'job_template_job_list'), + url(r'^(?P[0-9]+)/jobs/$', 'job_template_jobs_list'), ) -jobs_urls = patterns('ansibleworks.main.views', +job_urls = patterns('ansibleworks.main.views', url(r'^$', 'job_list'), url(r'^(?P[0-9]+)/$', 'job_detail'), url(r'^(?P[0-9]+)/start/$', 'job_start'), url(r'^(?P[0-9]+)/cancel/$', 'job_cancel'), - url(r'^(?P[0-9]+)/job_host_summaries/$', 'job_job_host_summary_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'), - url(r'^(?P[0-9]+)/job_events/$', 'job_job_event_list'), + url(r'^(?P[0-9]+)/job_host_summaries/$', 'job_job_host_summaries_list'), + url(r'^(?P[0-9]+)/job_events/$', 'job_job_events_list'), ) job_host_summary_urls = patterns('ansibleworks.main.views', url(r'^(?P[0-9]+)/$', 'job_host_summary_detail'), ) -job_events_urls = patterns('ansibleworks.main.views', +job_event_urls = patterns('ansibleworks.main.views', url(r'^$', 'job_event_list'), url(r'^(?P[0-9]+)/$', 'job_event_detail'), ) @@ -116,20 +114,20 @@ v1_urls = patterns('ansibleworks.main.views', url(r'^$', 'api_v1_root_view'), url(r'^config/$', 'api_v1_config_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'^teams/', include(teams_urls)), + url(r'^me/$', 'user_me_list'), + url(r'^organizations/', include(organization_urls)), + url(r'^users/', include(user_urls)), + url(r'^projects/', include(project_urls)), + url(r'^teams/', include(team_urls)), url(r'^inventories/', include(inventory_urls)), - url(r'^hosts/', include(hosts_urls)), - url(r'^groups/', include(groups_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'^hosts/', include(host_urls)), + url(r'^groups/', include(group_urls)), + url(r'^credentials/', include(credential_urls)), + url(r'^permissions/', include(permission_urls)), + url(r'^job_templates/', include(job_template_urls)), + url(r'^jobs/', include(job_urls)), url(r'^job_host_summaries/', include(job_host_summary_urls)), - url(r'^job_events/', include(job_events_urls)), + url(r'^job_events/', include(job_event_urls)), ) urlpatterns = patterns('ansibleworks.main.views', diff --git a/ansibleworks/main/views.py b/ansibleworks/main/views.py index 9ac02b546d..61078eeb2f 100644 --- a/ansibleworks/main/views.py +++ b/ansibleworks/main/views.py @@ -82,18 +82,18 @@ class ApiV1RootView(APIView): ''' list top level resources ''' data = dict( - organizations = reverse('main:organizations_list'), - users = reverse('main:users_list'), - projects = reverse('main:projects_list'), - teams = reverse('main:teams_list'), - credentials = reverse('main:credentials_list'), + organizations = reverse('main:organization_list'), + users = reverse('main:user_list'), + projects = reverse('main:project_list'), + teams = reverse('main:team_list'), + credentials = reverse('main:credential_list'), inventory = reverse('main:inventory_list'), - groups = reverse('main:groups_list'), - hosts = reverse('main:hosts_list'), + groups = reverse('main:group_list'), + hosts = reverse('main:host_list'), job_templates = reverse('main:job_template_list'), jobs = reverse('main:job_list'), authtoken = reverse('main:auth_token_view'), - me = reverse('main:users_me_list'), + me = reverse('main:user_me_list'), config = reverse('main:api_v1_config_view'), ) return Response(data) @@ -152,7 +152,7 @@ class AuthTokenView(ObtainAuthToken): renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES -class OrganizationsList(BaseList): +class OrganizationList(BaseList): model = Organization serializer_class = OrganizationSerializer @@ -175,13 +175,13 @@ class OrganizationsList(BaseList): users__in = [ self.request.user ] ).distinct() -class OrganizationsDetail(BaseDetail): +class OrganizationDetail(BaseDetail): model = Organization serializer_class = OrganizationSerializer permission_classes = (CustomRbac,) -class OrganizationsInventoriesList(BaseSubList): +class OrganizationInventoriesList(BaseSubList): model = Inventory serializer_class = InventorySerializer @@ -198,7 +198,7 @@ class OrganizationsInventoriesList(BaseSubList): raise PermissionDenied() return Inventory.objects.filter(organization__in=[organization]) -class OrganizationsUsersList(BaseSubList): +class OrganizationUsersList(BaseSubList): model = User serializer_class = UserSerializer @@ -216,7 +216,7 @@ class OrganizationsUsersList(BaseSubList): raise PermissionDenied() return User.objects.filter(organizations__in = [ organization ]) -class OrganizationsAdminsList(BaseSubList): +class OrganizationAdminsList(BaseSubList): model = User serializer_class = UserSerializer @@ -234,7 +234,7 @@ class OrganizationsAdminsList(BaseSubList): raise PermissionDenied() return User.objects.filter(admin_of_organizations__in = [ organization ]) -class OrganizationsProjectsList(BaseSubList): +class OrganizationProjectsList(BaseSubList): model = Project serializer_class = ProjectSerializer @@ -252,7 +252,7 @@ class OrganizationsProjectsList(BaseSubList): raise PermissionDenied() return Project.objects.filter(organizations__in = [ organization ]) -class OrganizationsTeamsList(BaseSubList): +class OrganizationTeamsList(BaseSubList): model = Team serializer_class = TeamSerializer @@ -271,7 +271,7 @@ class OrganizationsTeamsList(BaseSubList): raise PermissionDenied() return Team.objects.filter(organization = organization) -class TeamsList(BaseList): +class TeamList(BaseList): model = Team serializer_class = TeamSerializer @@ -294,13 +294,13 @@ class TeamsList(BaseList): users__in = [ self.request.user ] ).distinct() -class TeamsDetail(BaseDetail): +class TeamDetail(BaseDetail): model = Team serializer_class = TeamSerializer permission_classes = (CustomRbac,) -class TeamsUsersList(BaseSubList): +class TeamUsersList(BaseSubList): model = User serializer_class = UserSerializer @@ -323,7 +323,7 @@ class TeamsUsersList(BaseSubList): return base raise PermissionDenied() -class TeamsPermissionsList(BaseSubList): +class TeamPermissionsList(BaseSubList): model = Permission serializer_class = PermissionSerializer @@ -345,7 +345,7 @@ class TeamsPermissionsList(BaseSubList): raise PermissionDenied() -class TeamsProjectsList(BaseSubList): +class TeamProjectsList(BaseSubList): model = Project serializer_class = ProjectSerializer @@ -369,7 +369,7 @@ class TeamsProjectsList(BaseSubList): raise PermissionDenied() -class TeamsCredentialsList(BaseSubList): +class TeamCredentialsList(BaseSubList): model = Credential serializer_class = CredentialSerializer @@ -392,7 +392,7 @@ class TeamsCredentialsList(BaseSubList): return project_credentials.distinct() -class ProjectsList(BaseList): +class ProjectList(BaseList): model = Project serializer_class = ProjectSerializer @@ -417,19 +417,19 @@ class ProjectsList(BaseList): organizations__in = my_orgs ).distinct() -class ProjectsDetail(BaseDetail): +class ProjectDetail(BaseDetail): model = Project serializer_class = ProjectSerializer permission_classes = (CustomRbac,) -class ProjectsDetailPlaybooks(generics.RetrieveAPIView): +class ProjectDetailPlaybooks(generics.RetrieveAPIView): model = Project serializer_class = ProjectPlaybooksSerializer permission_classes = (CustomRbac,) -class ProjectsOrganizationsList(BaseSubList): +class ProjectOrganizationsList(BaseSubList): model = Organization serializer_class = OrganizationSerializer @@ -445,7 +445,7 @@ class ProjectsOrganizationsList(BaseSubList): raise PermissionDenied() return Organization.objects.filter(projects__in = [ project ]) -class UsersList(BaseList): +class UserList(BaseList): model = User serializer_class = UserSerializer @@ -454,7 +454,7 @@ class UsersList(BaseList): def post(self, request, *args, **kwargs): password = request.DATA.get('password', None) - result = super(UsersList, self).post(request, *args, **kwargs) + result = super(UserList, self).post(request, *args, **kwargs) if password: pk = result.data['id'] user = User.objects.get(pk=pk) @@ -472,7 +472,7 @@ class UsersList(BaseList): same_team = base.filter(teams__in = self.request.user.teams.all()).distinct() return mine | admin_of | same_team -class UsersMeList(BaseList): +class UserMeList(BaseList): model = User serializer_class = UserSerializer @@ -488,7 +488,7 @@ class UsersMeList(BaseList): ''' a quick way to find my user record ''' return User.objects.filter(pk=self.request.user.pk) -class UsersTeamsList(BaseSubList): +class UserTeamsList(BaseSubList): model = Team serializer_class = TeamSerializer @@ -505,7 +505,7 @@ class UsersTeamsList(BaseSubList): raise PermissionDenied() return Team.objects.filter(users__in = [ user ]) -class UsersPermissionsList(BaseSubList): +class UserPermissionsList(BaseSubList): model = Permission serializer_class = PermissionSerializer @@ -523,7 +523,7 @@ class UsersPermissionsList(BaseSubList): raise PermissionDenied() return Permission.objects.filter(user=user) -class UsersProjectsList(BaseSubList): +class UserProjectsList(BaseSubList): model = Project serializer_class = ProjectSerializer @@ -541,7 +541,7 @@ class UsersProjectsList(BaseSubList): teams = user.teams.all() return Project.objects.filter(teams__in = teams) -class UsersCredentialsList(BaseSubList): +class UserCredentialsList(BaseSubList): model = Credential serializer_class = CredentialSerializer @@ -562,7 +562,7 @@ class UsersCredentialsList(BaseSubList): ) return user.credentials.distinct() | project_credentials.distinct() -class UsersOrganizationsList(BaseSubList): +class UserOrganizationsList(BaseSubList): model = Organization serializer_class = OrganizationSerializer @@ -579,7 +579,7 @@ class UsersOrganizationsList(BaseSubList): raise PermissionDenied() return Organization.objects.filter(users__in = [ user ]) -class UsersAdminOrganizationsList(BaseSubList): +class UserAdminOfOrganizationsList(BaseSubList): model = Organization serializer_class = OrganizationSerializer @@ -596,7 +596,7 @@ class UsersAdminOrganizationsList(BaseSubList): raise PermissionDenied() return Organization.objects.filter(admins__in = [ user ]) -class UsersDetail(BaseDetail): +class UserDetail(BaseDetail): model = User serializer_class = UserSerializer @@ -612,7 +612,7 @@ class UsersDetail(BaseDetail): obj.save() request.DATA.pop('password') -class CredentialsList(BaseList): +class CredentialList(BaseList): model = Credential serializer_class = CredentialSerializer @@ -622,13 +622,13 @@ class CredentialsList(BaseList): def get_queryset(self): return get_user_queryset(self.request.user, self.model) -class CredentialsDetail(BaseDetail): +class CredentialDetail(BaseDetail): model = Credential serializer_class = CredentialSerializer permission_classes = (CustomRbac,) -class PermissionsDetail(BaseDetail): +class PermissionDetail(BaseDetail): model = Permission serializer_class = PermissionSerializer @@ -666,7 +666,7 @@ class InventoryDetail(BaseDetail): serializer_class = InventorySerializer permission_classes = (CustomRbac,) -class HostsList(BaseList): +class HostList(BaseList): model = Host serializer_class = HostSerializer @@ -694,7 +694,7 @@ class HostsList(BaseList): ).distinct() return admin_of | has_user_perms | has_team_perms -class HostsDetail(BaseDetail): +class HostDetail(BaseDetail): model = Host serializer_class = HostSerializer @@ -721,7 +721,71 @@ class InventoryHostsList(BaseSubList): # FIXME: verify that you can can_read permission on the inventory is required return base.all() -class GroupsList(BaseList): +class HostGroupsList(BaseSubList): + ''' the list of groups a host is directly a member of ''' + + model = Group + serializer_class = GroupSerializer + permission_classes = (CustomRbac,) + parent_model = Host + relationship = 'groups' + postable = True + inject_primary_key_on_post_as = 'host' + filter_fields = ('name',) + + def get_queryset(self): + + parent = Host.objects.get(pk=self.kwargs['pk']) + + # FIXME: verify read permissions on this object are still required at a higher level + + base = parent.groups + if self.request.user.is_superuser: + return base.all() + admin_of = base.filter(inventory__organization__admins__in = [ self.request.user ]).distinct() + has_user_perms = base.filter( + inventory__permissions__user__in = [ self.request.user ], + inventory__permissions__permission_type__in = PERMISSION_TYPES_ALLOWING_INVENTORY_READ, + ).distinct() + has_team_perms = base.filter( + inventory__permissions__team__in = self.request.user.teams.all(), + inventory__permissions__permission_type__in = PERMISSION_TYPES_ALLOWING_INVENTORY_READ, + ).distinct() + return admin_of | has_user_perms | has_team_perms + +class HostAllGroupsList(BaseSubList): + ''' the list of all groups of which the host is directly or indirectly a member ''' + + model = Group + serializer_class = GroupSerializer + permission_classes = (CustomRbac,) + parent_model = Host + relationship = 'groups' + filter_fields = ('name',) + + def get_queryset(self): + + parent = Host.objects.get(pk=self.kwargs['pk']) + + # FIXME: verify read permissions on this object are still required at a higher level + + base = parent.all_groups + + if self.request.user.is_superuser: + return base.all() + + admin_of = base.filter(inventory__organization__admins__in = [ self.request.user ]).distinct() + has_user_perms = base.filter( + inventory__permissions__user__in = [ self.request.user ], + inventory__permissions__permission_type__in = PERMISSION_TYPES_ALLOWING_INVENTORY_READ, + ).distinct() + has_team_perms = base.filter( + inventory__permissions__team__in = self.request.user.teams.all(), + inventory__permissions__permission_type__in = PERMISSION_TYPES_ALLOWING_INVENTORY_READ, + ).distinct() + return admin_of | has_user_perms | has_team_perms + +class GroupList(BaseList): model = Group serializer_class = GroupSerializer @@ -749,7 +813,7 @@ class GroupsList(BaseList): ).distinct() return admin_of | has_user_perms | has_team_perms -class GroupsChildrenList(BaseSubList): +class GroupChildrenList(BaseSubList): model = Group serializer_class = GroupSerializer @@ -782,7 +846,7 @@ class GroupsChildrenList(BaseSubList): ).distinct() return admin_of | has_user_perms | has_team_perms -class GroupsHostsList(BaseSubList): +class GroupHostsList(BaseSubList): ''' the list of hosts directly below a group ''' model = Host @@ -815,7 +879,7 @@ class GroupsHostsList(BaseSubList): return admin_of | has_user_perms | has_team_perms -class GroupsAllHostsList(BaseSubList): +class GroupAllHostsList(BaseSubList): ''' the list of all hosts below a group, even including subgroups ''' model = Host @@ -848,7 +912,7 @@ class GroupsAllHostsList(BaseSubList): return admin_of | has_user_perms | has_team_perms -class GroupsDetail(BaseDetail): +class GroupDetail(BaseDetail): model = Group serializer_class = GroupSerializer @@ -901,14 +965,14 @@ class InventoryVariableDetail(BaseDetail): permission_classes = (CustomRbac,) is_variable_data = True # Special flag for RBAC -class HostsVariableDetail(BaseDetail): +class HostVariableDetail(BaseDetail): model = Host serializer_class = HostVariableDataSerializer permission_classes = (CustomRbac,) is_variable_data = True # Special flag for RBAC -class GroupsVariableDetail(BaseDetail): +class GroupVariableDetail(BaseDetail): model = Group serializer_class = GroupVariableDataSerializer @@ -931,7 +995,7 @@ class JobTemplateDetail(BaseDetail): serializer_class = JobTemplateSerializer permission_classes = (CustomRbac,) -class JobTemplateJobList(BaseSubList): +class JobTemplateJobsList(BaseSubList): model = Job serializer_class = JobSerializer @@ -1019,7 +1083,7 @@ class JobCancel(generics.RetrieveAPIView): else: return Response(status=405) -class BaseJobHostSummaryList(generics.ListAPIView): +class BaseJobHostSummariesList(generics.ListAPIView): model = JobHostSummary serializer_class = JobHostSummarySerializer @@ -1034,15 +1098,15 @@ class BaseJobHostSummaryList(generics.ListAPIView): parent_obj = get_object_or_404(self.parent_model, pk=self.kwargs['pk']) return getattr(parent_obj, self.relationship) -class HostJobHostSummaryList(BaseJobHostSummaryList): +class HostJobHostSummariesList(BaseJobHostSummariesList): parent_model = Host -class GroupJobHostSummaryList(BaseJobHostSummaryList): +class GroupJobHostSummariesList(BaseJobHostSummariesList): parent_model = Group -class JobJobHostSummaryList(BaseJobHostSummaryList): +class JobJobHostSummariesList(BaseJobHostSummariesList): parent_model = Job @@ -1069,7 +1133,7 @@ class JobEventDetail(generics.RetrieveAPIView): serializer_class = JobEventSerializer permission_classes = (CustomRbac,) -class BaseJobEventList(generics.ListAPIView): +class BaseJobEventsList(generics.ListAPIView): model = JobEvent serializer_class = JobEventSerializer @@ -1082,15 +1146,15 @@ class BaseJobEventList(generics.ListAPIView): parent_obj = get_object_or_404(self.parent_model, pk=self.kwargs['pk']) return getattr(parent_obj, self.relationship) -class HostJobEventList(BaseJobEventList): +class HostJobEventsList(BaseJobEventsList): parent_model = Host -class GroupJobEventList(BaseJobEventList): +class GroupJobEventsList(BaseJobEventsList): parent_model = Group -class JobJobEventList(BaseJobEventList): +class JobJobEventsList(BaseJobEventsList): parent_model = Job