mirror of
https://github.com/ansible/awx.git
synced 2026-02-24 14:36:00 -03:30
Merge pull request #363 from AlanCoding/org_smart_inv
backend of org-scoped smart inventories
This commit is contained in:
@@ -602,9 +602,20 @@ class InventoryAccess(BaseAccess):
|
|||||||
|
|
||||||
@check_superuser
|
@check_superuser
|
||||||
def can_admin(self, obj, data):
|
def can_admin(self, obj, data):
|
||||||
|
# Host filter may only be modified by org admin level
|
||||||
|
org_admin_mandatory = False
|
||||||
|
new_host_filter = data.get('host_filter', None) if data else None
|
||||||
|
if new_host_filter == '': # Provision for field 'blank' behavior
|
||||||
|
new_host_filter = None
|
||||||
|
if new_host_filter != obj.host_filter:
|
||||||
|
org_admin_mandatory = True
|
||||||
# Verify that the user has access to the new organization if moving an
|
# Verify that the user has access to the new organization if moving an
|
||||||
# inventory to a new organization. Otherwise, just check for admin permission.
|
# inventory to a new organization. Otherwise, just check for admin permission.
|
||||||
return self.check_related('organization', Organization, data, obj=obj) and self.user in obj.admin_role
|
return (
|
||||||
|
self.check_related('organization', Organization, data, obj=obj,
|
||||||
|
mandatory=org_admin_mandatory) and
|
||||||
|
self.user in obj.admin_role
|
||||||
|
)
|
||||||
|
|
||||||
@check_superuser
|
@check_superuser
|
||||||
def can_update(self, obj):
|
def can_update(self, obj):
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ class HostManager(models.Manager):
|
|||||||
hasattr(self.instance, 'kind')):
|
hasattr(self.instance, 'kind')):
|
||||||
if self.instance.kind == 'smart' and self.instance.host_filter is not None:
|
if self.instance.kind == 'smart' and self.instance.host_filter is not None:
|
||||||
q = SmartFilter.query_from_string(self.instance.host_filter)
|
q = SmartFilter.query_from_string(self.instance.host_filter)
|
||||||
|
if self.instance.organization_id:
|
||||||
|
q = q.filter(inventory__organization=self.instance.organization_id)
|
||||||
# If we are using host_filters, disable the core_filters, this allows
|
# If we are using host_filters, disable the core_filters, this allows
|
||||||
# us to access all of the available Host entries, not just the ones associated
|
# us to access all of the available Host entries, not just the ones associated
|
||||||
# with a specific FK/relation.
|
# with a specific FK/relation.
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from awx.main.models import (
|
|||||||
InventorySource,
|
InventorySource,
|
||||||
InventoryUpdate,
|
InventoryUpdate,
|
||||||
)
|
)
|
||||||
|
from awx.main.utils.filters import SmartFilter
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
@@ -109,3 +110,17 @@ class TestHostManager:
|
|||||||
organization=organization,
|
organization=organization,
|
||||||
host_filter='inventory_sources__source=ec2')
|
host_filter='inventory_sources__source=ec2')
|
||||||
assert len(smart_inventory.hosts.all()) == 0
|
assert len(smart_inventory.hosts.all()) == 0
|
||||||
|
|
||||||
|
def test_host_distinctness(self, setup_inventory_groups, organization):
|
||||||
|
"""
|
||||||
|
two criteria would both yield the same host, check that we only get 1 copy here
|
||||||
|
"""
|
||||||
|
assert (
|
||||||
|
list(SmartFilter.query_from_string('name=single_host or name__startswith=single_')) ==
|
||||||
|
[Host.objects.get(name='single_host')]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Things we can not easily test due to SQLite backend:
|
||||||
|
# 2 organizations with host of same name only has 1 entry in smart inventory
|
||||||
|
# smart inventory in 1 organization does not include host from another
|
||||||
|
# smart inventory correctly returns hosts in filter in same organization
|
||||||
|
|||||||
@@ -174,3 +174,17 @@ def test_inventory_source_org_admin_schedule_access(org_admin, inventory_source)
|
|||||||
assert access.get_queryset()
|
assert access.get_queryset()
|
||||||
assert access.can_read(schedule)
|
assert access.can_read(schedule)
|
||||||
assert access.can_change(schedule, {'rrule': 'DTSTART:20151117T050000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=2'})
|
assert access.can_change(schedule, {'rrule': 'DTSTART:20151117T050000Z RRULE:FREQ=DAILY;INTERVAL=1;COUNT=2'})
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def smart_inventory(organization):
|
||||||
|
return organization.inventories.create(name="smart-inv", kind="smart")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
class TestSmartInventory:
|
||||||
|
|
||||||
|
def test_host_filter_edit(self, smart_inventory, rando, org_admin):
|
||||||
|
assert InventoryAccess(org_admin).can_admin(smart_inventory, {'host_filter': 'search=foo'})
|
||||||
|
smart_inventory.admin_role.members.add(rando)
|
||||||
|
assert not InventoryAccess(rando).can_admin(smart_inventory, {'host_filter': 'search=foo'})
|
||||||
|
|||||||
@@ -220,8 +220,8 @@ class TestHostInsights():
|
|||||||
class TestInventoryHostsList(object):
|
class TestInventoryHostsList(object):
|
||||||
|
|
||||||
def test_host_list_smart_inventory(self, mocker):
|
def test_host_list_smart_inventory(self, mocker):
|
||||||
Inventory = namedtuple('Inventory', ['kind', 'host_filter', 'hosts'])
|
Inventory = namedtuple('Inventory', ['kind', 'host_filter', 'hosts', 'organization_id'])
|
||||||
obj = Inventory(kind='smart', host_filter='localhost', hosts=HostManager())
|
obj = Inventory(kind='smart', host_filter='localhost', hosts=HostManager(), organization_id=None)
|
||||||
obj.hosts.instance = obj
|
obj.hosts.instance = obj
|
||||||
|
|
||||||
with mock.patch.object(InventoryHostsList, 'get_parent_object', return_value=obj):
|
with mock.patch.object(InventoryHostsList, 'get_parent_object', return_value=obj):
|
||||||
|
|||||||
Reference in New Issue
Block a user