1
0
mirror of https://github.com/ansible/awx.git synced 2024-10-27 09:25:10 +03:00

Remove custom inventory script API

This commit is contained in:
Alan Rominger 2021-04-12 13:57:03 -04:00
parent 855cb162b7
commit 38352063e8
No known key found for this signature in database
GPG Key ID: C2D7EAAA12B63559
32 changed files with 60 additions and 467 deletions

View File

@ -51,7 +51,6 @@ from awx.main.models import (
Credential,
CredentialInputSource,
CredentialType,
CustomInventoryScript,
ExecutionEnvironment,
Group,
Host,
@ -167,7 +166,6 @@ SUMMARIZABLE_FK_FIELDS = {
'current_update': DEFAULT_SUMMARY_FIELDS + ('status', 'failed', 'license_error'),
'current_job': DEFAULT_SUMMARY_FIELDS + ('status', 'failed', 'license_error'),
'inventory_source': ('id', 'name', 'source', 'last_updated', 'status'),
'custom_inventory_script': DEFAULT_SUMMARY_FIELDS,
'source_script': DEFAULT_SUMMARY_FIELDS,
'role': ('id', 'role_field'),
'notification_template': DEFAULT_SUMMARY_FIELDS,
@ -1984,49 +1982,6 @@ class GroupVariableDataSerializer(BaseVariableDataSerializer):
model = Group
class CustomInventoryScriptSerializer(BaseSerializer):
script = serializers.CharField(trim_whitespace=False)
show_capabilities = ['edit', 'delete', 'copy']
capabilities_prefetch = [{'edit': 'admin'}]
class Meta:
model = CustomInventoryScript
fields = ('*', "script", "organization")
def validate_script(self, value):
if not value.startswith("#!"):
raise serializers.ValidationError(_('Script must begin with a hashbang sequence: i.e.... #!/usr/bin/env python'))
return value
def to_representation(self, obj):
ret = super(CustomInventoryScriptSerializer, self).to_representation(obj)
if obj is None:
return ret
request = self.context.get('request', None)
if (
request.user not in obj.admin_role
and not request.user.is_superuser
and not request.user.is_system_auditor
and not (obj.organization is not None and request.user in obj.organization.auditor_role)
):
ret['script'] = None
return ret
def get_related(self, obj):
res = super(CustomInventoryScriptSerializer, self).get_related(obj)
res.update(
dict(
object_roles=self.reverse('api:inventory_script_object_roles_list', kwargs={'pk': obj.pk}),
copy=self.reverse('api:inventory_script_copy', kwargs={'pk': obj.pk}),
)
)
if obj.organization:
res['organization'] = self.reverse('api:organization_detail', kwargs={'pk': obj.organization.pk})
return res
class InventorySourceOptionsSerializer(BaseSerializer):
credential = DeprecatedCredentialField(help_text=_('Cloud credential to use for inventory updates.'))
@ -2053,8 +2008,6 @@ class InventorySourceOptionsSerializer(BaseSerializer):
res = super(InventorySourceOptionsSerializer, self).get_related(obj)
if obj.credential: # TODO: remove when 'credential' field is removed
res['credential'] = self.reverse('api:credential_detail', kwargs={'pk': obj.credential})
if obj.source_script:
res['source_script'] = self.reverse('api:inventory_script_detail', kwargs={'pk': obj.source_script.pk})
return res
def validate_source_vars(self, value):

View File

@ -1,16 +0,0 @@
# Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved.
from django.conf.urls import url
from awx.api.views import InventoryScriptList, InventoryScriptDetail, InventoryScriptObjectRolesList, InventoryScriptCopy
urls = [
url(r'^$', InventoryScriptList.as_view(), name='inventory_script_list'),
url(r'^(?P<pk>[0-9]+)/$', InventoryScriptDetail.as_view(), name='inventory_script_detail'),
url(r'^(?P<pk>[0-9]+)/object_roles/$', InventoryScriptObjectRolesList.as_view(), name='inventory_script_object_roles_list'),
url(r'^(?P<pk>[0-9]+)/copy/$', InventoryScriptCopy.as_view(), name='inventory_script_copy'),
]
__all__ = ['urls']

View File

@ -43,7 +43,6 @@ from .host import urls as host_urls
from .group import urls as group_urls
from .inventory_source import urls as inventory_source_urls
from .inventory_update import urls as inventory_update_urls
from .inventory_script import urls as inventory_script_urls
from .credential_type import urls as credential_type_urls
from .credential import urls as credential_urls
from .credential_input_source import urls as credential_input_source_urls
@ -111,7 +110,6 @@ v2_urls = [
url(r'^groups/', include(group_urls)),
url(r'^inventory_sources/', include(inventory_source_urls)),
url(r'^inventory_updates/', include(inventory_update_urls)),
url(r'^inventory_scripts/', include(inventory_script_urls)),
url(r'^credentials/', include(credential_urls)),
url(r'^roles/', include(role_urls)),
url(r'^job_templates/', include(job_template_urls)),

View File

@ -152,10 +152,6 @@ from awx.api.views.inventory import ( # noqa
InventoryList,
InventoryDetail,
InventoryUpdateEventsList,
InventoryScriptList,
InventoryScriptDetail,
InventoryScriptObjectRolesList,
InventoryScriptCopy,
InventoryList,
InventoryDetail,
InventoryActivityStreamList,

View File

@ -25,8 +25,6 @@ from awx.main.models import (
InstanceGroup,
InventoryUpdateEvent,
InventoryUpdate,
InventorySource,
CustomInventoryScript,
)
from awx.api.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView, SubListAPIView, SubListAttachDetachAPIView, ResourceAccessList, CopyAPIView
@ -36,7 +34,6 @@ from awx.api.serializers import (
RoleSerializer,
InstanceGroupSerializer,
InventoryUpdateEventSerializer,
CustomInventoryScriptSerializer,
JobTemplateSerializer,
)
from awx.api.views.mixin import RelatedJobsPreventDeleteMixin, ControlledByScmMixin
@ -58,55 +55,6 @@ class InventoryUpdateEventsList(SubListAPIView):
return super(InventoryUpdateEventsList, self).finalize_response(request, response, *args, **kwargs)
class InventoryScriptList(ListCreateAPIView):
deprecated = True
model = CustomInventoryScript
serializer_class = CustomInventoryScriptSerializer
class InventoryScriptDetail(RetrieveUpdateDestroyAPIView):
deprecated = True
model = CustomInventoryScript
serializer_class = CustomInventoryScriptSerializer
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
can_delete = request.user.can_access(self.model, 'delete', instance)
if not can_delete:
raise PermissionDenied(_("Cannot delete inventory script."))
for inv_src in InventorySource.objects.filter(source_script=instance):
inv_src.source_script = None
inv_src.save()
return super(InventoryScriptDetail, self).destroy(request, *args, **kwargs)
class InventoryScriptObjectRolesList(SubListAPIView):
deprecated = True
model = Role
serializer_class = RoleSerializer
parent_model = CustomInventoryScript
search_fields = ('role_field', 'content_type__model')
def get_queryset(self):
po = self.get_parent_object()
content_type = ContentType.objects.get_for_model(self.parent_model)
return Role.objects.filter(content_type=content_type, object_id=po.pk)
class InventoryScriptCopy(CopyAPIView):
deprecated = True
model = CustomInventoryScript
copy_return_serializer_class = CustomInventoryScriptSerializer
class InventoryList(ListCreateAPIView):
model = Inventory

View File

@ -100,7 +100,6 @@ class ApiVersionRootView(APIView):
data['tokens'] = reverse('api:o_auth2_token_list', request=request)
data['metrics'] = reverse('api:metrics_view', request=request)
data['inventory'] = reverse('api:inventory_list', request=request)
data['inventory_scripts'] = reverse('api:inventory_script_list', request=request)
data['inventory_sources'] = reverse('api:inventory_source_list', request=request)
data['inventory_updates'] = reverse('api:inventory_update_list', request=request)
data['groups'] = reverse('api:group_list', request=request)

View File

@ -34,7 +34,6 @@ from awx.main.models import (
Credential,
CredentialType,
CredentialInputSource,
CustomInventoryScript,
ExecutionEnvironment,
Group,
Host,
@ -465,7 +464,7 @@ class BaseAccess(object):
if display_method == 'schedule':
user_capabilities['schedule'] = user_capabilities['start']
continue
elif display_method == 'delete' and not isinstance(obj, (User, UnifiedJob, CustomInventoryScript, CredentialInputSource)):
elif display_method == 'delete' and not isinstance(obj, (User, UnifiedJob, CredentialInputSource)):
user_capabilities['delete'] = user_capabilities['edit']
continue
elif display_method == 'copy' and isinstance(obj, (Group, Host)):
@ -2755,33 +2754,6 @@ class ActivityStreamAccess(BaseAccess):
return False
class CustomInventoryScriptAccess(BaseAccess):
model = CustomInventoryScript
prefetch_related = ('created_by', 'modified_by', 'organization')
def filtered_queryset(self):
return self.model.accessible_objects(self.user, 'read_role').all()
@check_superuser
def can_add(self, data):
if not data: # So the browseable API will work
return Organization.accessible_objects(self.user, 'admin_role').exists()
return self.check_related('organization', Organization, data, mandatory=True)
@check_superuser
def can_admin(self, obj, data=None):
return self.check_related('organization', Organization, data, obj=obj) and self.user in obj.admin_role
@check_superuser
def can_change(self, obj, data):
return self.can_admin(obj, data=data)
@check_superuser
def can_delete(self, obj):
return self.can_admin(obj)
class RoleAccess(BaseAccess):
"""
- I can see roles when

View File

@ -135,7 +135,6 @@ def counts(since, **kwargs):
models.WorkflowJobTemplate,
models.Host,
models.Schedule,
models.CustomInventoryScript,
models.NotificationTemplate,
):
counts[camelcase_to_underscore(cls.__name__)] = cls.objects.count()
@ -347,29 +346,29 @@ def _copy_table(table, query, path):
@register('events_table', '1.2', format='csv', description=_('Automation task records'), expensive=events_slicing)
def events_table(since, full_path, until, **kwargs):
def query(event_data):
return f'''COPY (SELECT main_jobevent.id,
return f'''COPY (SELECT main_jobevent.id,
main_jobevent.created,
main_jobevent.modified,
main_jobevent.uuid,
main_jobevent.parent_uuid,
main_jobevent.event,
main_jobevent.event,
{event_data}->'task_action' AS task_action,
(CASE WHEN event = 'playbook_on_stats' THEN event_data END) as playbook_on_stats,
main_jobevent.failed,
main_jobevent.changed,
main_jobevent.playbook,
main_jobevent.failed,
main_jobevent.changed,
main_jobevent.playbook,
main_jobevent.play,
main_jobevent.task,
main_jobevent.role,
main_jobevent.job_id,
main_jobevent.host_id,
main_jobevent.role,
main_jobevent.job_id,
main_jobevent.host_id,
main_jobevent.host_name,
CAST({event_data}->>'start' AS TIMESTAMP WITH TIME ZONE) AS start,
CAST({event_data}->>'end' AS TIMESTAMP WITH TIME ZONE) AS end,
{event_data}->'duration' AS duration,
{event_data}->'res'->'warnings' AS warnings,
{event_data}->'res'->'deprecations' AS deprecations
FROM main_jobevent
FROM main_jobevent
WHERE (main_jobevent.id > {since} AND main_jobevent.id <= {until})
ORDER BY main_jobevent.id ASC) TO STDOUT WITH CSV HEADER'''
@ -386,22 +385,22 @@ def unified_jobs_table(since, full_path, until, **kwargs):
django_content_type.model,
main_unifiedjob.organization_id,
main_organization.name as organization_name,
main_job.inventory_id,
main_job.inventory_id,
main_inventory.name as inventory_name,
main_unifiedjob.created,
main_unifiedjob.name,
main_unifiedjob.unified_job_template_id,
main_unifiedjob.launch_type,
main_unifiedjob.schedule_id,
main_unifiedjob.execution_node,
main_unifiedjob.controller_node,
main_unifiedjob.cancel_flag,
main_unifiedjob.status,
main_unifiedjob.failed,
main_unifiedjob.started,
main_unifiedjob.finished,
main_unifiedjob.elapsed,
main_unifiedjob.job_explanation,
main_unifiedjob.created,
main_unifiedjob.name,
main_unifiedjob.unified_job_template_id,
main_unifiedjob.launch_type,
main_unifiedjob.schedule_id,
main_unifiedjob.execution_node,
main_unifiedjob.controller_node,
main_unifiedjob.cancel_flag,
main_unifiedjob.status,
main_unifiedjob.failed,
main_unifiedjob.started,
main_unifiedjob.finished,
main_unifiedjob.elapsed,
main_unifiedjob.job_explanation,
main_unifiedjob.instance_group_id,
main_unifiedjob.installed_collections,
main_unifiedjob.ansible_version
@ -422,21 +421,21 @@ def unified_jobs_table(since, full_path, until, **kwargs):
@register('unified_job_template_table', '1.0', format='csv', description=_('Data on job templates'))
def unified_job_template_table(since, full_path, **kwargs):
unified_job_template_query = '''COPY (SELECT main_unifiedjobtemplate.id,
unified_job_template_query = '''COPY (SELECT main_unifiedjobtemplate.id,
main_unifiedjobtemplate.polymorphic_ctype_id,
django_content_type.model,
main_unifiedjobtemplate.created,
main_unifiedjobtemplate.modified,
main_unifiedjobtemplate.created_by_id,
main_unifiedjobtemplate.modified_by_id,
main_unifiedjobtemplate.name,
main_unifiedjobtemplate.current_job_id,
main_unifiedjobtemplate.last_job_id,
main_unifiedjobtemplate.last_job_failed,
main_unifiedjobtemplate.last_job_run,
main_unifiedjobtemplate.next_job_run,
main_unifiedjobtemplate.next_schedule_id,
main_unifiedjobtemplate.status
main_unifiedjobtemplate.created,
main_unifiedjobtemplate.modified,
main_unifiedjobtemplate.created_by_id,
main_unifiedjobtemplate.modified_by_id,
main_unifiedjobtemplate.name,
main_unifiedjobtemplate.current_job_id,
main_unifiedjobtemplate.last_job_id,
main_unifiedjobtemplate.last_job_failed,
main_unifiedjobtemplate.last_job_run,
main_unifiedjobtemplate.next_job_run,
main_unifiedjobtemplate.next_schedule_id,
main_unifiedjobtemplate.status
FROM main_unifiedjobtemplate, django_content_type
WHERE main_unifiedjobtemplate.polymorphic_ctype_id = django_content_type.id
ORDER BY main_unifiedjobtemplate.id ASC) TO STDOUT WITH CSV HEADER'''
@ -447,15 +446,15 @@ def unified_job_template_table(since, full_path, **kwargs):
def workflow_job_node_table(since, full_path, until, **kwargs):
workflow_job_node_query = '''COPY (SELECT main_workflowjobnode.id,
main_workflowjobnode.created,
main_workflowjobnode.modified,
main_workflowjobnode.job_id,
main_workflowjobnode.unified_job_template_id,
main_workflowjobnode.workflow_job_id,
main_workflowjobnode.inventory_id,
main_workflowjobnode.modified,
main_workflowjobnode.job_id,
main_workflowjobnode.unified_job_template_id,
main_workflowjobnode.workflow_job_id,
main_workflowjobnode.inventory_id,
success_nodes.nodes AS success_nodes,
failure_nodes.nodes AS failure_nodes,
always_nodes.nodes AS always_nodes,
main_workflowjobnode.do_not_run,
main_workflowjobnode.do_not_run,
main_workflowjobnode.all_parents_must_converge
FROM main_workflowjobnode
LEFT JOIN (
@ -483,12 +482,12 @@ def workflow_job_node_table(since, full_path, until, **kwargs):
@register('workflow_job_template_node_table', '1.0', format='csv', description=_('Data on workflows'))
def workflow_job_template_node_table(since, full_path, **kwargs):
workflow_job_template_node_query = '''COPY (SELECT main_workflowjobtemplatenode.id,
workflow_job_template_node_query = '''COPY (SELECT main_workflowjobtemplatenode.id,
main_workflowjobtemplatenode.created,
main_workflowjobtemplatenode.modified,
main_workflowjobtemplatenode.unified_job_template_id,
main_workflowjobtemplatenode.workflow_job_template_id,
main_workflowjobtemplatenode.inventory_id,
main_workflowjobtemplatenode.modified,
main_workflowjobtemplatenode.unified_job_template_id,
main_workflowjobtemplatenode.workflow_job_template_id,
main_workflowjobtemplatenode.inventory_id,
success_nodes.nodes AS success_nodes,
failure_nodes.nodes AS failure_nodes,
always_nodes.nodes AS always_nodes,

View File

@ -28,7 +28,6 @@ def create_roles(apps, schema_editor):
'Inventory',
'Project',
'Credential',
'CustomInventoryScript',
'JobTemplate',
]
]

View File

@ -12,7 +12,7 @@ from awx.main.models.unified_jobs import UnifiedJob, UnifiedJobTemplate, StdoutM
from awx.main.models.organization import Organization, Profile, Team, UserSessionMembership # noqa
from awx.main.models.credential import Credential, CredentialType, CredentialInputSource, ManagedCredentialType, build_safe_env # noqa
from awx.main.models.projects import Project, ProjectUpdate # noqa
from awx.main.models.inventory import CustomInventoryScript, Group, Host, Inventory, InventorySource, InventoryUpdate, SmartInventoryMembership # noqa
from awx.main.models.inventory import Group, Host, Inventory, InventorySource, InventoryUpdate, SmartInventoryMembership # noqa
from awx.main.models.jobs import ( # noqa
Job,
JobHostSummary,
@ -224,7 +224,6 @@ activity_stream_registrar.connect(AdHocCommand)
# activity_stream_registrar.connect(JobEvent)
# activity_stream_registrar.connect(Profile)
activity_stream_registrar.connect(Schedule)
activity_stream_registrar.connect(CustomInventoryScript)
activity_stream_registrar.connect(NotificationTemplate)
activity_stream_registrar.connect(Notification)
activity_stream_registrar.connect(Label)

View File

@ -74,6 +74,7 @@ class ActivityStream(models.Model):
unified_job = models.ManyToManyField("UnifiedJob", blank=True, related_name='activity_stream_as_unified_job+')
ad_hoc_command = models.ManyToManyField("AdHocCommand", blank=True)
schedule = models.ManyToManyField("Schedule", blank=True)
# TODO: migrate away
custom_inventory_script = models.ManyToManyField("CustomInventoryScript", blank=True)
execution_environment = models.ManyToManyField("ExecutionEnvironment", blank=True)
notification_template = models.ManyToManyField("NotificationTemplate", blank=True)

View File

@ -52,7 +52,7 @@ from awx.main.utils import _inventory_updates
from awx.main.utils.safe_yaml import sanitize_jinja
__all__ = ['Inventory', 'Host', 'Group', 'InventorySource', 'InventoryUpdate', 'CustomInventoryScript', 'SmartInventoryMembership']
__all__ = ['Inventory', 'Host', 'Group', 'InventorySource', 'InventoryUpdate', 'SmartInventoryMembership']
logger = logging.getLogger('awx.main.models.inventory')
@ -845,6 +845,7 @@ class InventorySourceOptions(BaseModel):
blank=True,
default='',
)
# TODO: migrate away
source_script = models.ForeignKey(
'CustomInventoryScript',
null=True,

View File

@ -378,7 +378,6 @@ def model_serializer_mapping():
models.Group: serializers.GroupSerializer,
models.InstanceGroup: serializers.InstanceGroupSerializer,
models.InventorySource: serializers.InventorySourceSerializer,
models.CustomInventoryScript: serializers.CustomInventoryScriptSerializer,
models.Credential: serializers.CredentialSerializer,
models.Team: serializers.TeamSerializer,
models.Project: serializers.ProjectSerializer,

View File

@ -12,7 +12,6 @@ def test_empty():
"active_sessions": 0,
"active_host_count": 0,
"credential": 0,
"custom_inventory_script": 0,
"custom_virtualenvs": 0, # dev env ansible3
"host": 0,
"inventory": 0,
@ -48,7 +47,6 @@ def test_database_counts(organization_factory, job_template_factory, workflow_jo
rrule="DTSTART;TZID=America/New_York:20300504T150000",
unified_job_template=jt.job_template,
).save()
models.CustomInventoryScript(organization=objs.organization).save()
counts = collectors.counts(None)
for key in (
@ -62,7 +60,6 @@ def test_database_counts(organization_factory, job_template_factory, workflow_jo
"workflow_job_template",
"host",
"schedule",
"custom_inventory_script",
):
assert counts[key] == 1

View File

@ -18,7 +18,6 @@ EXPECTED_VALUES = {
'awx_hosts_total': 1.0,
'awx_hosts_total': 1.0,
'awx_schedules_total': 1.0,
'awx_inventory_scripts_total': 1.0,
'awx_sessions_total': 0.0,
'awx_sessions_total': 0.0,
'awx_sessions_total': 0.0,
@ -44,7 +43,6 @@ def test_metrics_counts(organization_factory, job_template_factory, workflow_job
models.Team(organization=objs.organization).save()
models.Host(inventory=jt.inventory).save()
models.Schedule(rrule='DTSTART;TZID=America/New_York:20300504T150000', unified_job_template=jt.job_template).save()
models.CustomInventoryScript(organization=objs.organization).save()
output = metrics()
gauges = text_string_to_metric_families(output.decode('UTF-8'))

View File

@ -144,14 +144,10 @@ def test_async_inventory_deletion_deletes_related_jt(delete, get, job_template,
assert jdata['inventory'] is None
@pytest.mark.parametrize('order_by', ('script', '-script', 'script,pk', '-script,pk'))
@pytest.mark.parametrize('order_by', ('extra_vars', '-extra_vars', 'extra_vars,pk', '-extra_vars,pk'))
@pytest.mark.django_db
def test_list_cannot_order_by_unsearchable_field(get, organization, alice, order_by):
for i, script in enumerate(('#!/bin/a', '#!/bin/b', '#!/bin/c')):
custom_script = organization.custom_inventory_scripts.create(name="I%d" % i, script=script)
custom_script.admin_role.members.add(alice)
get(reverse('api:inventory_script_list'), alice, QUERY_STRING='order_by=%s' % order_by, expect=403)
get(reverse('api:jobs_list'), alice, QUERY_STRING='order_by=%s' % order_by, expect=403)
@pytest.mark.parametrize("role_field,expected_status_code", [(None, 403), ('admin_role', 201), ('update_role', 403), ('adhoc_role', 403), ('use_role', 403)])

View File

@ -26,7 +26,7 @@ from rest_framework.test import (
from awx.main.models.credential import CredentialType, Credential
from awx.main.models.jobs import JobTemplate, SystemJobTemplate
from awx.main.models.inventory import Group, Inventory, InventoryUpdate, InventorySource, CustomInventoryScript
from awx.main.models.inventory import Group, Inventory, InventoryUpdate, InventorySource
from awx.main.models.organization import (
Organization,
Team,
@ -540,11 +540,6 @@ def inventory_update(inventory_source):
return InventoryUpdate.objects.create(inventory_source=inventory_source, source=inventory_source.source)
@pytest.fixture
def inventory_script(organization):
return CustomInventoryScript.objects.create(name='test inv script', organization=organization, script='#!/usr/bin/python')
@pytest.fixture
def host(group, inventory):
return group.hosts.create(name='single-host', inventory=inventory)

View File

@ -216,16 +216,3 @@ def test_notification_template_copy(post, get, notification_template_with_encryp
assert decrypt_field(notification_template_with_encrypt, 'notification_configuration', 'token') == decrypt_field(
notification_template_copy, 'notification_configuration', 'token'
)
@pytest.mark.django_db
def test_inventory_script_copy(post, get, inventory_script, organization, alice):
inventory_script.organization.auditor_role.members.add(alice)
assert get(reverse('api:inventory_script_copy', kwargs={'pk': inventory_script.pk}), alice, expect=200).data['can_copy'] is False
inventory_script.organization.admin_role.members.add(alice)
assert get(reverse('api:inventory_script_copy', kwargs={'pk': inventory_script.pk}), alice, expect=200).data['can_copy'] is True
is_copy_pk = post(reverse('api:inventory_script_copy', kwargs={'pk': inventory_script.pk}), {'name': 'copied inv script'}, alice, expect=201).data['id']
inventory_script_copy = type(inventory_script).objects.get(pk=is_copy_pk)
assert inventory_script_copy.created_by == alice
assert inventory_script_copy.name == 'copied inv script'
assert inventory_script_copy.organization == organization

View File

@ -8,7 +8,6 @@ from awx.api.versioning import reverse
from awx.main.middleware import URLModificationMiddleware
from awx.main.models import ( # noqa
Credential,
CustomInventoryScript,
Group,
Host,
Instance,
@ -204,19 +203,6 @@ def test_inventory_source(get, admin_user):
assert response.data['related']['named_url'].endswith('/test_source++/')
@pytest.mark.django_db
def test_inventory_script(get, admin_user):
test_script = CustomInventoryScript.objects.create(name='test_script')
url = reverse('api:inventory_script_detail', kwargs={'pk': test_script.pk})
response = get(url, user=admin_user, expect=200)
assert response.data['related']['named_url'].endswith('/test_script++/')
test_org = Organization.objects.create(name='test_org')
test_script.organization = test_org
test_script.save()
response = get(url, user=admin_user, expect=200)
assert response.data['related']['named_url'].endswith('/test_script++test_org/')
@pytest.mark.django_db
def test_credential(get, admin_user, credentialtype_ssh):
test_cred = Credential.objects.create(name='test_cred', credential_type=credentialtype_ssh)

View File

@ -2,7 +2,6 @@ import pytest
from awx.main.models import (
Host,
CustomInventoryScript,
Schedule,
)
from awx.main.access import (
@ -10,56 +9,10 @@ from awx.main.access import (
InventorySourceAccess,
HostAccess,
InventoryUpdateAccess,
CustomInventoryScriptAccess,
ScheduleAccess,
)
@pytest.mark.django_db
def test_custom_inv_script_access(organization, user):
u = user('user', False)
ou = user('oadm', False)
custom_inv = CustomInventoryScript.objects.create(name='test', script='test', description='test')
custom_inv.organization = organization
custom_inv.save()
assert u not in custom_inv.read_role
organization.member_role.members.add(u)
assert u in custom_inv.read_role
organization.admin_role.members.add(ou)
assert ou in custom_inv.admin_role
@pytest.fixture
def custom_inv(organization):
return CustomInventoryScript.objects.create(name='test', script='test', description='test', organization=organization)
@pytest.mark.django_db
def test_modify_inv_script_foreign_org_admin(org_admin, organization, organization_factory, project, custom_inv):
other_org = organization_factory('not-my-org').organization
access = CustomInventoryScriptAccess(org_admin)
assert not access.can_change(custom_inv, {'organization': other_org.pk, 'name': 'new-project'})
@pytest.mark.django_db
def test_org_member_inventory_script_permissions(org_member, organization, custom_inv):
access = CustomInventoryScriptAccess(org_member)
assert access.can_read(custom_inv)
assert not access.can_delete(custom_inv)
assert not access.can_change(custom_inv, {'name': 'ed-test'})
@pytest.mark.django_db
def test_copy_only_admin(org_member, organization, custom_inv):
custom_inv.admin_role.members.add(org_member)
access = CustomInventoryScriptAccess(org_member)
assert not access.can_copy(custom_inv)
assert access.get_user_capabilities(custom_inv, method_list=['edit', 'delete', 'copy']) == {'edit': True, 'delete': True, 'copy': False}
@pytest.mark.django_db
@pytest.mark.parametrize("role", ["admin_role", "inventory_admin_role"])
def test_access_admin(role, organization, inventory, user):

View File

@ -1,25 +1,12 @@
# Python
import pytest
from unittest import mock
from unittest.mock import PropertyMock
# AWX
from awx.api.serializers import (
CustomInventoryScriptSerializer,
InventorySourceSerializer,
)
from awx.main.models import (
CustomInventoryScript,
InventorySource,
User,
)
# DRF
from rest_framework.request import Request
from rest_framework.test import (
APIRequestFactory,
force_authenticate,
)
from awx.main.models import InventorySource
@pytest.fixture
@ -30,34 +17,6 @@ def inventory_source(mocker):
return obj
class TestCustomInventoryScriptSerializer(object):
@pytest.mark.parametrize(
"superuser,sysaudit,admin_role,value",
((True, False, False, '#!/python'), (False, True, False, '#!/python'), (False, False, True, '#!/python'), (False, False, False, None)),
)
def test_to_representation_orphan(self, superuser, sysaudit, admin_role, value):
with mock.patch.object(CustomInventoryScriptSerializer, 'get_summary_fields', return_value={}):
with mock.patch.object(User, 'is_system_auditor', return_value=sysaudit):
user = User(username="root", is_superuser=superuser)
roles = [user] if admin_role else []
with mock.patch('awx.main.models.CustomInventoryScript.admin_role', new_callable=PropertyMock, return_value=roles), mock.patch(
'awx.api.serializers.settings'
):
cis = CustomInventoryScript(pk=1, script=value)
serializer = CustomInventoryScriptSerializer()
factory = APIRequestFactory()
wsgi_request = factory.post("/inventory_script/1", {'id': 1}, format="json")
force_authenticate(wsgi_request, user)
request = Request(wsgi_request)
serializer.context['request'] = request
representation = serializer.to_representation(cis)
assert representation['script'] == value
@mock.patch('awx.api.serializers.UnifiedJobTemplateSerializer.get_related', lambda x, y: {})
@mock.patch('awx.api.serializers.InventorySourceOptionsSerializer.get_related', lambda x, y: {})
class TestInventorySourceSerializerGetRelated(object):

View File

@ -7,7 +7,6 @@ from awx.api.filters import FieldLookupBackend, OrderByBackend, get_field_from_p
from awx.main.models import (
AdHocCommand,
ActivityStream,
CustomInventoryScript,
Credential,
Job,
JobTemplate,
@ -106,7 +105,6 @@ def test_filter_on_password_field(password_field, lookup_suffix):
(WorkflowJob, 'survey_passwords__icontains'),
(JobTemplate, 'survey_spec__icontains'),
(WorkflowJobTemplate, 'survey_spec__icontains'),
(CustomInventoryScript, 'script__icontains'),
(ActivityStream, 'o_auth2_application__client_secret__gt'),
(OAuth2Application, 'grant__code__gt'),
],

View File

@ -32,7 +32,6 @@ class TestApiRootView:
'teams',
'credentials',
'inventory',
'inventory_scripts',
'inventory_sources',
'groups',
'hosts',

View File

@ -30,7 +30,6 @@ from awx.main.models import (
ProjectUpdate,
UnifiedJob,
User,
CustomInventoryScript,
build_safe_env,
)
from awx.main.models.credential import ManagedCredentialType
@ -1631,55 +1630,6 @@ class TestInventoryUpdateCredentials(TestJobExecution):
assert 'AWS_ACCESS_KEY_ID' not in env
assert 'AWS_SECRET_ACCESS_KEY' not in env
@pytest.mark.parametrize('with_credential', [True, False])
def test_custom_source(self, with_credential, mocker, inventory_update, private_data_dir):
task = tasks.RunInventoryUpdate()
task.instance = inventory_update
inventory_update.source = 'custom'
inventory_update.source_vars = '{"FOO": "BAR"}'
inventory_update.source_script = CustomInventoryScript(script='#!/bin/sh\necho "Hello, World!"')
if with_credential:
azure_rm = CredentialType.defaults['azure_rm']()
def get_creds():
cred = Credential(
pk=1,
credential_type=azure_rm,
inputs={
'client': 'some-client',
'secret': 'some-secret',
'tenant': 'some-tenant',
'subscription': 'some-subscription',
},
)
return [cred]
inventory_update.get_extra_credentials = get_creds
else:
inventory_update.get_extra_credentials = mocker.Mock(return_value=[])
inventory_update.get_cloud_credential = mocker.Mock(return_value=None)
env = task.build_env(inventory_update, private_data_dir, False)
args = task.build_args(inventory_update, private_data_dir, {})
credentials = task.build_credentials_list(inventory_update)
for credential in credentials:
if credential:
credential.credential_type.inject_credential(credential, env, {}, [], private_data_dir)
assert '-i' in ' '.join(args)
script = args[args.index('-i') + 1]
host_script = script.replace('/runner', private_data_dir)
with open(host_script, 'r') as f:
assert f.read() == inventory_update.source_script.script
assert env['FOO'] == 'BAR'
if with_credential:
assert env['AZURE_CLIENT_ID'] == 'some-client'
assert env['AZURE_SECRET'] == 'some-secret'
assert env['AZURE_TENANT'] == 'some-tenant'
assert env['AZURE_SUBSCRIPTION_ID'] == 'some-subscription'
def test_ec2_source(self, private_data_dir, inventory_update, mocker):
task = tasks.RunInventoryUpdate()
task.instance = inventory_update

View File

@ -256,8 +256,6 @@ def main():
inventory_source_fields['execution_environment'] = module.resolve_name_to_id('execution_environments', ee)
if source_project is not None:
inventory_source_fields['source_project'] = module.resolve_name_to_id('projects', source_project)
if source_script is not None:
inventory_source_fields['source_script'] = module.resolve_name_to_id('inventory_scripts', source_script)
OPTIONAL_VARS = (
'description',

View File

@ -66,12 +66,6 @@ options:
default: []
type: list
elements: str
inventory_script:
description:
- List of inventory script names to export
default: []
type: list
elements: str
inventory:
description:
- List of inventory names to export
@ -153,7 +147,6 @@ def main():
credential=dict(type='list', default=[], elements='str'),
credential_type=dict(type='list', default=[], elements='str'),
inventory=dict(type='list', default=[], elements='str'),
inventory_script=dict(type='list', default=[], elements='str'),
job_template=dict(type='list', default=[], elements='str'),
notification_template=dict(type='list', default=[], elements='str'),
organization=dict(type='list', default=[], elements='str'),

View File

@ -69,7 +69,9 @@ no_api_parameter_ok = {
# When this tool was created we were not feature complete. Adding something in here indicates a module
# that needs to be developed. If the module is found on the file system it will auto-detect that the
# work is being done and will bypass this check. At some point this module should be removed from this list.
needs_development = ['tower_inventory_script', 'tower_workflow_approval']
needs_development = [
'tower_workflow_approval',
]
needs_param_development = {
'tower_host': ['instance_id'],
}

View File

@ -125,64 +125,6 @@ class Inventories(page.PageList, Inventory):
page.register_page([resources.inventories, resources.related_inventories], Inventories)
class InventoryScript(HasCopy, HasCreate, base.Base):
dependencies = [Organization]
def payload(self, organization, **kwargs):
payload = PseudoNamespace(
name=kwargs.get('name') or 'Inventory Script - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10),
organization=organization.id,
script=kwargs.get('script') or self._generate_script(),
)
return payload
def create_payload(self, name='', description='', organization=Organization, script='', **kwargs):
self.create_and_update_dependencies(organization)
payload = self.payload(name=name, description=description, organization=self.ds.organization, script=script, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload
def create(self, name='', description='', organization=Organization, script='', **kwargs):
payload = self.create_payload(name=name, description=description, organization=organization, script=script, **kwargs)
return self.update_identity(InventoryScripts(self.connection).post(payload))
def _generate_script(self):
script = '\n'.join(
[
'#!/usr/bin/env python',
'# -*- coding: utf-8 -*-',
'import json',
'inventory = dict()',
'inventory["{0}"] = dict()',
'inventory["{0}"]["hosts"] = list()',
'inventory["{0}"]["hosts"].append("{1}")',
'inventory["{0}"]["hosts"].append("{2}")',
'inventory["{0}"]["hosts"].append("{3}")',
'inventory["{0}"]["hosts"].append("{4}")',
'inventory["{0}"]["hosts"].append("{5}")',
'inventory["{0}"]["vars"] = dict(ansible_host="127.0.0.1", ansible_connection="local")',
'print(json.dumps(inventory))',
]
)
group_name = re.sub(r"[\']", "", "group_{}".format(random_title(non_ascii=False)))
host_names = [re.sub(r"[\':]", "", "host_{}".format(random_utf8())) for _ in range(5)]
return script.format(group_name, *host_names)
page.register_page([resources.inventory_script, (resources.inventory_scripts, 'post'), (resources.inventory_script_copy, 'post')], InventoryScript)
class InventoryScripts(page.PageList, InventoryScript):
pass
page.register_page([resources.inventory_scripts], InventoryScripts)
class Group(HasCreate, HasVariables, base.Base):
dependencies = [Inventory]
@ -408,8 +350,6 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
if credential:
credential = self.ds.credential
if source_script:
source_script = self.ds.inventory_script
if project:
project = self.ds.project

View File

@ -65,9 +65,6 @@ class Resources(object):
_inventory_related_root_groups = r'inventories/\d+/root_groups/'
_inventory_related_script = r'inventories/\d+/script/'
_inventory_related_update_inventory_sources = r'inventories/\d+/update_inventory_sources/'
_inventory_script = r'inventory_scripts/\d+/'
_inventory_script_copy = r'inventory_scripts/\d+/copy/'
_inventory_scripts = 'inventory_scripts/'
_inventory_source = r'inventory_sources/\d+/'
_inventory_source_schedule = r'inventory_sources/\d+/schedules/\d+/'
_inventory_source_schedules = r'inventory_sources/\d+/schedules/'

View File

@ -39,7 +39,6 @@ def delete_all(v):
v.projects,
v.inventory,
v.hosts,
v.inventory_scripts,
v.labels,
v.credentials,
v.teams,

View File

@ -349,7 +349,6 @@ class RoleMixin(object):
['organizations', 'organization'],
['projects', 'project'],
['inventories', 'inventory'],
['inventory_scripts', 'inventory_script'],
['teams', 'team'],
['credentials', 'credential'],
['job_templates', 'job_template'],

View File

@ -23,7 +23,6 @@ DEPRECATED_RESOURCES = {
'instances': 'instance',
'instance_groups': 'instance_group',
'inventory': 'inventories',
'inventory_scripts': 'inventory_script',
'inventory_sources': 'inventory_source',
'inventory_updates': 'inventory_update',
'jobs': 'job',