diff --git a/README.md b/README.md index e24e851ce1..145a690900 100644 --- a/README.md +++ b/README.md @@ -14,20 +14,20 @@ Contributing ------------ - Refer to the [Contributing guide](./CONTRIBUTING.md) to get started developing, testing, and building AWX. -- All code submissions are done through pull requests against the `devel` branch. -- All contributors must use git commit --signoff for any commit to be merged, and agree that usage of --signoff constitutes agreement with the terms of [DCO 1.1](./DCO_1_1.md) -- Take care to make sure no merge commits are in the submission, and use `git rebase` vs `git merge` for this reason. -- If submitting a large code change, it's a good idea to join the `#ansible-awx` channel on irc.freenode.net, and talk about what you would like to do or add first. This not only helps everyone know what's going on, it also helps save time and effort, if the community decides some changes are needed. +- All code submissions are made through pull requests against the `devel` branch. +- All contributors must use git commit --signoff for any commit to be merged and agree that usage of --signoff constitutes agreement with the terms of [DCO 1.1](./DCO_1_1.md) +- Take care to make sure no merge commits are in the submission, and use `git rebase` vs. `git merge` for this reason. +- If submitting a large code change, it's a good idea to join the `#ansible-awx` channel on irc.freenode.net and talk about what you would like to do or add first. This not only helps everyone know what's going on, but it also helps save time and effort if the community decides some changes are needed. Reporting Issues ---------------- -If you're experiencing a problem that you feel is a bug in AWX, or have ideas for how to improve AWX, we encourage you to open an issue, and share your feedback. But before opening a new issue, we ask that you please take a look at our [Issues guide](./ISSUES.md). +If you're experiencing a problem that you feel is a bug in AWX or have ideas for improving AWX, we encourage you to open an issue and share your feedback. But before opening a new issue, we ask that you please take a look at our [Issues guide](./ISSUES.md). Code of Conduct --------------- -We ask all of our community members and contributors to adhere to the [Ansible code of conduct](http://docs.ansible.com/ansible/latest/community/code_of_conduct.html). If you have questions, or need assistance, please reach out to our community team at [codeofconduct@ansible.com](mailto:codeofconduct@ansible.com) +We ask all of our community members and contributors to adhere to the [Ansible code of conduct](http://docs.ansible.com/ansible/latest/community/code_of_conduct.html). If you have questions or need assistance, please reach out to our community team at [codeofconduct@ansible.com](mailto:codeofconduct@ansible.com) Get Involved ------------ @@ -41,4 +41,3 @@ License ------- [Apache v2](./LICENSE.md) - diff --git a/awx/api/views/organization.py b/awx/api/views/organization.py index 06172af79f..d03dfcc86f 100644 --- a/awx/api/views/organization.py +++ b/awx/api/views/organization.py @@ -13,6 +13,7 @@ from django.utils.translation import ugettext_lazy as _ from awx.main.models import ( ActivityStream, Inventory, + Host, Project, JobTemplate, WorkflowJobTemplate, @@ -98,6 +99,7 @@ class OrganizationDetail(RelatedJobsPreventDeleteMixin, RetrieveUpdateDestroyAPI organization__id=org_id).count() org_counts['job_templates'] = JobTemplate.accessible_objects(**access_kwargs).filter( organization__id=org_id).count() + org_counts['hosts'] = Host.objects.org_active_count(org_id) full_context['related_field_counts'] = {} full_context['related_field_counts'][org_id] = org_counts diff --git a/awx/main/models/__init__.py b/awx/main/models/__init__.py index 509378fcf5..87fa5d791f 100644 --- a/awx/main/models/__init__.py +++ b/awx/main/models/__init__.py @@ -81,10 +81,17 @@ User.add_to_class('accessible_objects', user_accessible_objects) def enforce_bigint_pk_migration(): + # + # NOTE: this function is not actually in use anymore, + # but has been intentionally kept for historical purposes, + # and to serve as an illustration if we ever need to perform + # bulk modification/migration of event data in the future. + # # see: https://github.com/ansible/awx/issues/6010 # look at all the event tables and verify that they have been fully migrated # from the *old* int primary key table to the replacement bigint table # if not, attempt to migrate them in the background + # for tblname in ( 'main_jobevent', 'main_inventoryupdateevent', 'main_projectupdateevent', 'main_adhoccommandevent', diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 1fb7d62cef..e6cc61b7dd 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -60,7 +60,7 @@ from awx.main.models import ( Inventory, InventorySource, SmartInventoryMembership, Job, AdHocCommand, ProjectUpdate, InventoryUpdate, SystemJob, JobEvent, ProjectUpdateEvent, InventoryUpdateEvent, AdHocCommandEvent, SystemJobEvent, - build_safe_env, enforce_bigint_pk_migration + build_safe_env ) from awx.main.constants import ACTIVE_STATES from awx.main.exceptions import AwxTaskError, PostRunError @@ -138,12 +138,6 @@ def dispatch_startup(): if Instance.objects.me().is_controller(): awx_isolated_heartbeat() - # at process startup, detect the need to migrate old event records from int - # to bigint; at *some point* in the future, once certain versions of AWX - # and Tower fall out of use/support, we can probably just _assume_ that - # everybody has moved to bigint, and remove this code entirely - enforce_bigint_pk_migration() - # Update Tower's rsyslog.conf file based on loggins settings in the db reconfigure_rsyslog() @@ -738,6 +732,12 @@ def update_host_smart_inventory_memberships(): @task(queue=get_local_queuename) def migrate_legacy_event_data(tblname): + # + # NOTE: this function is not actually in use anymore, + # but has been intentionally kept for historical purposes, + # and to serve as an illustration if we ever need to perform + # bulk modification/migration of event data in the future. + # if 'event' not in tblname: return with advisory_lock(f'bigint_migration_{tblname}', wait=False) as acquired: diff --git a/awx/main/tests/functional/api/test_organization_counts.py b/awx/main/tests/functional/api/test_organization_counts.py index f5221ef5f1..d45b1fa083 100644 --- a/awx/main/tests/functional/api/test_organization_counts.py +++ b/awx/main/tests/functional/api/test_organization_counts.py @@ -2,7 +2,7 @@ import pytest from awx.api.versioning import reverse -from awx.main.models import Project +from awx.main.models import Project, Host @pytest.fixture @@ -81,6 +81,8 @@ def test_org_counts_detail_admin(resourced_organization, user, get): assert response.status_code == 200 counts = response.data['summary_fields']['related_field_counts'] + assert counts['hosts'] == 0 + counts.pop('hosts') assert counts == COUNTS_PRIMES @@ -93,6 +95,8 @@ def test_org_counts_detail_member(resourced_organization, user, get): assert response.status_code == 200 counts = response.data['summary_fields']['related_field_counts'] + assert counts['hosts'] == 0 + counts.pop('hosts') assert counts == { 'users': COUNTS_PRIMES['users'], # Policy is that members can see other users and admins 'admins': COUNTS_PRIMES['admins'], @@ -111,6 +115,7 @@ def test_org_counts_list_admin(resourced_organization, user, get): assert response.status_code == 200 counts = response.data['results'][0]['summary_fields']['related_field_counts'] + assert 'hosts' not in counts # doesn't show in list view assert counts == COUNTS_PRIMES @@ -123,6 +128,7 @@ def test_org_counts_list_member(resourced_organization, user, get): assert response.status_code == 200 counts = response.data['results'][0]['summary_fields']['related_field_counts'] + assert 'hosts' not in counts # doesn't show in list view assert counts == { 'users': COUNTS_PRIMES['users'], # Policy is that members can see other users and admins @@ -145,6 +151,7 @@ def test_new_org_zero_counts(user, post): new_org_list = post_response.render().data counts_dict = new_org_list['summary_fields']['related_field_counts'] + assert 'hosts' not in counts_dict # doesn't show in list view assert counts_dict == COUNTS_ZEROS @@ -167,6 +174,19 @@ def test_two_organizations(resourced_organization, organizations, user, get): assert counts[org_id_zero] == COUNTS_ZEROS +@pytest.mark.django_db +def test_hosts_counted(resourced_organization, user, get): + admin_user = user('admin', True) + assert Host.objects.org_active_count(resourced_organization.id) == 0 + resourced_organization.inventories.first().hosts.create(name='Some Host') + assert Host.objects.org_active_count(resourced_organization.id) == 1 + response = get(reverse('api:organization_detail', kwargs={'pk': resourced_organization.pk}), admin_user) + assert response.status_code == 200 + + counts = response.data['summary_fields']['related_field_counts'] + assert counts['hosts'] == Host.objects.org_active_count(resourced_organization.id) == 1 + + @pytest.mark.django_db def test_scan_JT_counted(resourced_organization, user, get): admin_user = user('admin', True) @@ -180,7 +200,10 @@ def test_scan_JT_counted(resourced_organization, user, get): # Test detail view detail_response = get(reverse('api:organization_detail', kwargs={'pk': resourced_organization.pk}), admin_user) assert detail_response.status_code == 200 - assert detail_response.data['summary_fields']['related_field_counts'] == counts_dict + counts = detail_response.data['summary_fields']['related_field_counts'] + assert 'hosts' in counts + counts.pop('hosts') + assert counts == counts_dict @pytest.mark.django_db @@ -205,4 +228,7 @@ def test_JT_not_double_counted(resourced_organization, user, get): # Test detail view detail_response = get(reverse('api:organization_detail', kwargs={'pk': resourced_organization.pk}), admin_user) assert detail_response.status_code == 200 - assert detail_response.data['summary_fields']['related_field_counts'] == counts_dict + counts = detail_response.data['summary_fields']['related_field_counts'] + assert 'hosts' in counts + counts.pop('hosts') + assert counts == counts_dict