create new bulk host create endpoint

allow system admins, org admins, and inventory admins to bulk create
hosts.

Testing on an "open" licensed awx as system admin, I created 1000 hosts with 6 queries in ~ 0.15 seconds
Testing on an "open" licensed awx as organization admin, I created 1000 hosts with 11 queries in ~ 0.15 seconds

fix org max host check

also only do permission denied if license is a trial

add /api/v2/bulk to list bulk apis available

add api description templates

One motiviation to not take a list of hosts with mixed inventories is to
keep things simple re: RBAC and keeping a constant number of queries.

If there is great clamor for accepting list of hosts to insert into
arbitrary different inventories, we could probably make it happen - we'd
need to pop the inventory off of each of the hosts, run the
HostSerializer validate, then in top level BulkHostCreateSerializer
fetch all the inventories/check permissions/org host limits for those
inventories/etc. But that makes this that much more complicated.

add test for rbac access

test also helped me find a bug in a query, fixed that

add test to assert num queries scales as expected

also move other test to dedicated file

also test with super user like I meant to

record activity stream for the inventory

this records that a certain number of hosts were added by a certain user
we could consider if there is any other additional information we want
to include
This commit is contained in:
Elijah DeLee
2022-12-21 00:26:49 -05:00
parent a24aaba6bc
commit 752289e175
7 changed files with 282 additions and 1 deletions

View File

@@ -0,0 +1,86 @@
import pytest
from uuid import uuid4
from awx.api.versioning import reverse
import json
from contextlib import contextmanager
from django.test.utils import CaptureQueriesContext
from django.db import connections
@contextmanager
def withAssertNumQueriesLessThan(num_queries):
with CaptureQueriesContext(connections['default']) as context:
yield
fail_msg = f"\r\n{json.dumps(context.captured_queries, indent=4)}"
assert len(context.captured_queries) < num_queries, fail_msg
@pytest.mark.django_db
@pytest.mark.parametrize('num_hosts, num_queries', [(9, 15), (99, 20), (999, 25)])
def test_bulk_host_create_num_queries(organization, inventory, post, get, user, num_hosts, num_queries):
'''
If I am a...
org admin
inventory admin at org level
admin of a particular inventory
superuser
Bulk Host create should take under a certain number of queries
'''
inventory.organization = organization
inventory_admin = user('inventory_admin', False)
org_admin = user('org_admin', False)
org_inv_admin = user('org_admin', False)
superuser = user('admin', True)
for u in [org_admin, org_inv_admin, inventory_admin]:
organization.member_role.members.add(u)
organization.admin_role.members.add(org_admin)
organization.inventory_admin_role.members.add(org_inv_admin)
inventory.admin_role.members.add(inventory_admin)
for u in [org_admin, inventory_admin, org_inv_admin, superuser]:
hosts = [{'name': uuid4()} for i in range(num_hosts)]
with withAssertNumQueriesLessThan(num_queries):
bulk_host_create_response = post(reverse('api:bulk_host_create'), {'inventory': inventory.id, 'hosts': hosts}, u, expect=201).data
assert bulk_host_create_response['created'] == len(hosts), f"unexpected number of hosts created for user {u}"
@pytest.mark.django_db
def test_bulk_host_create_rbac(organization, inventory, post, get, user):
'''
If I am a...
org admin
inventory admin at org level
admin of a particular invenotry
... I can bulk add hosts
Everyone else cannot
'''
inventory.organization = organization
inventory_admin = user('inventory_admin', False)
org_admin = user('org_admin', False)
org_inv_admin = user('org_admin', False)
auditor = user('auditor', False)
member = user('member', False)
use_inv_member = user('member', False)
for u in [org_admin, org_inv_admin, auditor, member, inventory_admin, use_inv_member]:
organization.member_role.members.add(u)
organization.admin_role.members.add(org_admin)
organization.inventory_admin_role.members.add(org_inv_admin)
inventory.admin_role.members.add(inventory_admin)
inventory.use_role.members.add(use_inv_member)
organization.auditor_role.members.add(auditor)
for indx, u in enumerate([org_admin, inventory_admin, org_inv_admin]):
bulk_host_create_response = post(
reverse('api:bulk_host_create'), {'inventory': inventory.id, 'hosts': [{'name': f'foobar-{indx}'}]}, u, expect=201
).data
assert bulk_host_create_response['created'] == 1, f"unexpected number of hosts created for user {u}"
for indx, u in enumerate([member, auditor, use_inv_member]):
bulk_host_create_response = post(
reverse('api:bulk_host_create'), {'inventory': inventory.id, 'hosts': [{'name': f'foobar2-{indx}'}]}, u, expect=400
).data