mirror of
https://github.com/ansible/awx.git
synced 2026-05-20 07:17:40 -02:30
Added /api/v1/groups/N/hosts and /api/v1/groups/N/all_hosts/
This commit is contained in:
6
TODO.md
6
TODO.md
@@ -78,3 +78,9 @@ QUESTIONS
|
|||||||
* if creating a project, do we want to have an appliance style path for them, like
|
* if creating a project, do we want to have an appliance style path for them, like
|
||||||
/storage/projects/GUID ??? may want to keep somewhere else ?
|
/storage/projects/GUID ??? may want to keep somewhere else ?
|
||||||
|
|
||||||
|
MISC
|
||||||
|
----
|
||||||
|
|
||||||
|
when associating inventory objects, objects should share common inventory records (user_can_attach method should check)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,11 @@ class BaseSubList(BaseList):
|
|||||||
|
|
||||||
# now make sure we could have already attached the two together. If we could not have, raise an exception
|
# now make sure we could have already attached the two together. If we could not have, raise an exception
|
||||||
# such that the transaction does not commit.
|
# such that the transaction does not commit.
|
||||||
|
|
||||||
|
if main == obj:
|
||||||
|
# no attaching to yourself
|
||||||
|
raise PermissionDenied()
|
||||||
|
|
||||||
if self.__class__.parent_model != User:
|
if self.__class__.parent_model != User:
|
||||||
if not self.__class__.parent_model.can_user_attach(request.user, main, obj, self.__class__.relationship):
|
if not self.__class__.parent_model.can_user_attach(request.user, main, obj, self.__class__.relationship):
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ class InventoryTest(BaseTest):
|
|||||||
got = self.get(url5, expect=200, auth=self.get_other_credentials())
|
got = self.get(url5, expect=200, auth=self.get_other_credentials())
|
||||||
self.assertEquals(got['count'], 3)
|
self.assertEquals(got['count'], 3)
|
||||||
|
|
||||||
|
|
||||||
##################################################
|
##################################################
|
||||||
# GROUPS->inventories POST via subcollection
|
# GROUPS->inventories POST via subcollection
|
||||||
|
|
||||||
@@ -310,6 +311,38 @@ class InventoryTest(BaseTest):
|
|||||||
put = self.put(vdata1_url, data=vars_c, expect=200, auth=self.get_normal_credentials())
|
put = self.put(vdata1_url, data=vars_c, expect=200, auth=self.get_normal_credentials())
|
||||||
self.assertEquals(put, vars_c)
|
self.assertEquals(put, vars_c)
|
||||||
|
|
||||||
|
|
||||||
|
####################################################
|
||||||
|
# ADDING HOSTS TO GROUPS
|
||||||
|
|
||||||
|
groups = Group.objects.all()
|
||||||
|
hosts = Host.objects.all()
|
||||||
|
groups[0].hosts.add(Host.objects.get(pk=1))
|
||||||
|
groups[0].hosts.add(Host.objects.get(pk=3))
|
||||||
|
groups[0].save()
|
||||||
|
|
||||||
|
# access
|
||||||
|
url1 = '/api/v1/groups/1/hosts/'
|
||||||
|
data = self.get(url1, expect=200, auth=self.get_normal_credentials())
|
||||||
|
self.assertEquals(data['count'], 2)
|
||||||
|
self.assertEquals(data['results'][0]['id'], 1)
|
||||||
|
self.assertEquals(data['results'][1]['id'], 3)
|
||||||
|
|
||||||
|
# addition
|
||||||
|
got = self.get('/api/v1/hosts/2/', expect=200, auth=self.get_normal_credentials())
|
||||||
|
self.assertEquals(got['id'], 2)
|
||||||
|
posted = self.post('/api/v1/groups/1/hosts/', data=got, expect=204, auth=self.get_normal_credentials())
|
||||||
|
data = self.get(url1, expect=200, auth=self.get_normal_credentials())
|
||||||
|
self.assertEquals(data['count'], 3)
|
||||||
|
self.assertEquals(data['results'][1]['id'], 2)
|
||||||
|
|
||||||
|
# removal
|
||||||
|
got['disassociate'] = 1
|
||||||
|
posted = self.post('/api/v1/groups/1/hosts/', data=got, expect=204, auth=self.get_normal_credentials())
|
||||||
|
data = self.get(url1, expect=200, auth=self.get_normal_credentials())
|
||||||
|
self.assertEquals(data['count'], 2)
|
||||||
|
self.assertEquals(data['results'][1]['id'], 3)
|
||||||
|
|
||||||
####################################################
|
####################################################
|
||||||
# SUBGROUPS
|
# SUBGROUPS
|
||||||
|
|
||||||
@@ -329,6 +362,12 @@ class InventoryTest(BaseTest):
|
|||||||
permission_type = PERM_INVENTORY_WRITE
|
permission_type = PERM_INVENTORY_WRITE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# data used for testing listing all hosts that are transitive members of a group
|
||||||
|
g2 = Group.objects.get(pk=2)
|
||||||
|
nh = Host.objects.create(name='newhost.example.com', inventory=inv, created_by=User.objects.get(pk=1))
|
||||||
|
g2.hosts.add(nh)
|
||||||
|
g2.save()
|
||||||
|
|
||||||
# a super user can set subgroups
|
# a super user can set subgroups
|
||||||
subgroups_url = '/api/v1/groups/1/children/'
|
subgroups_url = '/api/v1/groups/1/children/'
|
||||||
child_url = '/api/v1/groups/2/'
|
child_url = '/api/v1/groups/2/'
|
||||||
@@ -356,6 +395,15 @@ class InventoryTest(BaseTest):
|
|||||||
checked = self.get(subgroups_url3, expect=200, auth=self.get_normal_credentials())
|
checked = self.get(subgroups_url3, expect=200, auth=self.get_normal_credentials())
|
||||||
self.assertEqual(checked['count'], 1)
|
self.assertEqual(checked['count'], 1)
|
||||||
|
|
||||||
|
# slight detour
|
||||||
|
# can see all hosts under a group, even if it has subgroups
|
||||||
|
# this URL is NOT postable
|
||||||
|
all_hosts = '/api/v1/groups/1/all_hosts/'
|
||||||
|
self.assertEqual(Group.objects.get(pk=1).hosts.count(), 2)
|
||||||
|
data = self.get(all_hosts, expect=200, auth=self.get_normal_credentials())
|
||||||
|
self.post(all_hosts, data=dict(id=123456, msg='spam'), expect=405, auth=self.get_normal_credentials())
|
||||||
|
self.assertEquals(data['count'], 3)
|
||||||
|
|
||||||
# now post it back to remove it, by adding the disassociate bit
|
# now post it back to remove it, by adding the disassociate bit
|
||||||
result = checked['results'][0]
|
result = checked['results'][0]
|
||||||
result['disassociate'] = 1
|
result['disassociate'] = 1
|
||||||
|
|||||||
@@ -578,6 +578,83 @@ class GroupsChildrenList(BaseSubList):
|
|||||||
).distinct()
|
).distinct()
|
||||||
return admin_of | has_user_perms | has_team_perms
|
return admin_of | has_user_perms | has_team_perms
|
||||||
|
|
||||||
|
class GroupsHostsList(BaseSubList):
|
||||||
|
''' the list of hosts directly below a group '''
|
||||||
|
|
||||||
|
model = Host
|
||||||
|
serializer_class = HostSerializer
|
||||||
|
permission_classes = (CustomRbac,)
|
||||||
|
parent_model = Group
|
||||||
|
relationship = 'hosts'
|
||||||
|
postable = True
|
||||||
|
inject_primary_key_on_post_as = 'group'
|
||||||
|
|
||||||
|
def _get_queryset(self):
|
||||||
|
|
||||||
|
parent = Group.objects.get(pk=self.kwargs['pk'])
|
||||||
|
|
||||||
|
# FIXME: verify read permissions on this object are still required at a higher level
|
||||||
|
|
||||||
|
base = parent.hosts
|
||||||
|
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 GroupsAllHostsList(BaseSubList):
|
||||||
|
''' the list of all hosts below a group, even including subgroups '''
|
||||||
|
|
||||||
|
model = Host
|
||||||
|
serializer_class = HostSerializer
|
||||||
|
permission_classes = (CustomRbac,)
|
||||||
|
parent_model = Group
|
||||||
|
relationship = 'hosts'
|
||||||
|
|
||||||
|
def _child_hosts(self, parent):
|
||||||
|
# TODO: should probably be a method on the model
|
||||||
|
result = parent.hosts.distinct()
|
||||||
|
if parent.children.count() == 0:
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
for child in parent.children.all():
|
||||||
|
if child == parent:
|
||||||
|
# shouldn't happen, but be prepared in case DB is weird
|
||||||
|
continue
|
||||||
|
result = result | self._child_hosts(child)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _get_queryset(self):
|
||||||
|
|
||||||
|
parent = Group.objects.get(pk=self.kwargs['pk'])
|
||||||
|
|
||||||
|
# FIXME: verify read permissions on this object are still required at a higher level
|
||||||
|
|
||||||
|
base = self._child_hosts(parent)
|
||||||
|
|
||||||
|
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 GroupsDetail(BaseDetail):
|
class GroupsDetail(BaseDetail):
|
||||||
|
|
||||||
model = Group
|
model = Group
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ views_GroupsList = views.GroupsList.as_view()
|
|||||||
views_GroupsDetail = views.GroupsDetail.as_view()
|
views_GroupsDetail = views.GroupsDetail.as_view()
|
||||||
views_GroupsVariableDetail = views.GroupsVariableDetail.as_view()
|
views_GroupsVariableDetail = views.GroupsVariableDetail.as_view()
|
||||||
views_GroupsChildrenList = views.GroupsChildrenList.as_view()
|
views_GroupsChildrenList = views.GroupsChildrenList.as_view()
|
||||||
|
views_GroupsAllHostsList = views.GroupsAllHostsList.as_view()
|
||||||
|
views_GroupsHostsList = views.GroupsHostsList.as_view()
|
||||||
|
|
||||||
# host service
|
# host service
|
||||||
views_HostsList = views.HostsList.as_view()
|
views_HostsList = views.HostsList.as_view()
|
||||||
@@ -147,6 +149,8 @@ urlpatterns = patterns('',
|
|||||||
url(r'^api/v1/groups/$', views_GroupsList),
|
url(r'^api/v1/groups/$', views_GroupsList),
|
||||||
url(r'^api/v1/groups/(?P<pk>[0-9]+)/$', views_GroupsDetail),
|
url(r'^api/v1/groups/(?P<pk>[0-9]+)/$', views_GroupsDetail),
|
||||||
url(r'^api/v1/groups/(?P<pk>[0-9]+)/children/$', views_GroupsChildrenList),
|
url(r'^api/v1/groups/(?P<pk>[0-9]+)/children/$', views_GroupsChildrenList),
|
||||||
|
url(r'^api/v1/groups/(?P<pk>[0-9]+)/hosts/$', views_GroupsHostsList),
|
||||||
|
url(r'^api/v1/groups/(?P<pk>[0-9]+)/all_hosts/$', views_GroupsAllHostsList),
|
||||||
|
|
||||||
# variable data
|
# variable data
|
||||||
url(r'^api/v1/hosts/(?P<pk>[0-9]+)/variable_data/$', views_HostsVariableDetail),
|
url(r'^api/v1/hosts/(?P<pk>[0-9]+)/variable_data/$', views_HostsVariableDetail),
|
||||||
|
|||||||
Reference in New Issue
Block a user