Remove custom inventory script API

This commit is contained in:
Alan Rominger
2021-04-12 13:57:03 -04:00
parent 855cb162b7
commit 38352063e8
32 changed files with 60 additions and 467 deletions

View File

@@ -51,7 +51,6 @@ from awx.main.models import (
Credential, Credential,
CredentialInputSource, CredentialInputSource,
CredentialType, CredentialType,
CustomInventoryScript,
ExecutionEnvironment, ExecutionEnvironment,
Group, Group,
Host, Host,
@@ -167,7 +166,6 @@ SUMMARIZABLE_FK_FIELDS = {
'current_update': DEFAULT_SUMMARY_FIELDS + ('status', 'failed', 'license_error'), 'current_update': DEFAULT_SUMMARY_FIELDS + ('status', 'failed', 'license_error'),
'current_job': DEFAULT_SUMMARY_FIELDS + ('status', 'failed', 'license_error'), 'current_job': DEFAULT_SUMMARY_FIELDS + ('status', 'failed', 'license_error'),
'inventory_source': ('id', 'name', 'source', 'last_updated', 'status'), 'inventory_source': ('id', 'name', 'source', 'last_updated', 'status'),
'custom_inventory_script': DEFAULT_SUMMARY_FIELDS,
'source_script': DEFAULT_SUMMARY_FIELDS, 'source_script': DEFAULT_SUMMARY_FIELDS,
'role': ('id', 'role_field'), 'role': ('id', 'role_field'),
'notification_template': DEFAULT_SUMMARY_FIELDS, 'notification_template': DEFAULT_SUMMARY_FIELDS,
@@ -1984,49 +1982,6 @@ class GroupVariableDataSerializer(BaseVariableDataSerializer):
model = Group 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): class InventorySourceOptionsSerializer(BaseSerializer):
credential = DeprecatedCredentialField(help_text=_('Cloud credential to use for inventory updates.')) 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) res = super(InventorySourceOptionsSerializer, self).get_related(obj)
if obj.credential: # TODO: remove when 'credential' field is removed if obj.credential: # TODO: remove when 'credential' field is removed
res['credential'] = self.reverse('api:credential_detail', kwargs={'pk': obj.credential}) 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 return res
def validate_source_vars(self, value): 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 .group import urls as group_urls
from .inventory_source import urls as inventory_source_urls from .inventory_source import urls as inventory_source_urls
from .inventory_update import urls as inventory_update_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_type import urls as credential_type_urls
from .credential import urls as credential_urls from .credential import urls as credential_urls
from .credential_input_source import urls as credential_input_source_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'^groups/', include(group_urls)),
url(r'^inventory_sources/', include(inventory_source_urls)), url(r'^inventory_sources/', include(inventory_source_urls)),
url(r'^inventory_updates/', include(inventory_update_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'^credentials/', include(credential_urls)),
url(r'^roles/', include(role_urls)), url(r'^roles/', include(role_urls)),
url(r'^job_templates/', include(job_template_urls)), url(r'^job_templates/', include(job_template_urls)),

View File

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

View File

@@ -25,8 +25,6 @@ from awx.main.models import (
InstanceGroup, InstanceGroup,
InventoryUpdateEvent, InventoryUpdateEvent,
InventoryUpdate, InventoryUpdate,
InventorySource,
CustomInventoryScript,
) )
from awx.api.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView, SubListAPIView, SubListAttachDetachAPIView, ResourceAccessList, CopyAPIView from awx.api.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView, SubListAPIView, SubListAttachDetachAPIView, ResourceAccessList, CopyAPIView
@@ -36,7 +34,6 @@ from awx.api.serializers import (
RoleSerializer, RoleSerializer,
InstanceGroupSerializer, InstanceGroupSerializer,
InventoryUpdateEventSerializer, InventoryUpdateEventSerializer,
CustomInventoryScriptSerializer,
JobTemplateSerializer, JobTemplateSerializer,
) )
from awx.api.views.mixin import RelatedJobsPreventDeleteMixin, ControlledByScmMixin 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) 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): class InventoryList(ListCreateAPIView):
model = Inventory model = Inventory

View File

@@ -100,7 +100,6 @@ class ApiVersionRootView(APIView):
data['tokens'] = reverse('api:o_auth2_token_list', request=request) data['tokens'] = reverse('api:o_auth2_token_list', request=request)
data['metrics'] = reverse('api:metrics_view', request=request) data['metrics'] = reverse('api:metrics_view', request=request)
data['inventory'] = reverse('api:inventory_list', 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_sources'] = reverse('api:inventory_source_list', request=request)
data['inventory_updates'] = reverse('api:inventory_update_list', request=request) data['inventory_updates'] = reverse('api:inventory_update_list', request=request)
data['groups'] = reverse('api:group_list', request=request) data['groups'] = reverse('api:group_list', request=request)

View File

@@ -34,7 +34,6 @@ from awx.main.models import (
Credential, Credential,
CredentialType, CredentialType,
CredentialInputSource, CredentialInputSource,
CustomInventoryScript,
ExecutionEnvironment, ExecutionEnvironment,
Group, Group,
Host, Host,
@@ -465,7 +464,7 @@ class BaseAccess(object):
if display_method == 'schedule': if display_method == 'schedule':
user_capabilities['schedule'] = user_capabilities['start'] user_capabilities['schedule'] = user_capabilities['start']
continue 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'] user_capabilities['delete'] = user_capabilities['edit']
continue continue
elif display_method == 'copy' and isinstance(obj, (Group, Host)): elif display_method == 'copy' and isinstance(obj, (Group, Host)):
@@ -2755,33 +2754,6 @@ class ActivityStreamAccess(BaseAccess):
return False 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): class RoleAccess(BaseAccess):
""" """
- I can see roles when - I can see roles when

View File

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

View File

@@ -28,7 +28,6 @@ def create_roles(apps, schema_editor):
'Inventory', 'Inventory',
'Project', 'Project',
'Credential', 'Credential',
'CustomInventoryScript',
'JobTemplate', '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.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.credential import Credential, CredentialType, CredentialInputSource, ManagedCredentialType, build_safe_env # noqa
from awx.main.models.projects import Project, ProjectUpdate # 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 from awx.main.models.jobs import ( # noqa
Job, Job,
JobHostSummary, JobHostSummary,
@@ -224,7 +224,6 @@ activity_stream_registrar.connect(AdHocCommand)
# activity_stream_registrar.connect(JobEvent) # activity_stream_registrar.connect(JobEvent)
# activity_stream_registrar.connect(Profile) # activity_stream_registrar.connect(Profile)
activity_stream_registrar.connect(Schedule) activity_stream_registrar.connect(Schedule)
activity_stream_registrar.connect(CustomInventoryScript)
activity_stream_registrar.connect(NotificationTemplate) activity_stream_registrar.connect(NotificationTemplate)
activity_stream_registrar.connect(Notification) activity_stream_registrar.connect(Notification)
activity_stream_registrar.connect(Label) 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+') unified_job = models.ManyToManyField("UnifiedJob", blank=True, related_name='activity_stream_as_unified_job+')
ad_hoc_command = models.ManyToManyField("AdHocCommand", blank=True) ad_hoc_command = models.ManyToManyField("AdHocCommand", blank=True)
schedule = models.ManyToManyField("Schedule", blank=True) schedule = models.ManyToManyField("Schedule", blank=True)
# TODO: migrate away
custom_inventory_script = models.ManyToManyField("CustomInventoryScript", blank=True) custom_inventory_script = models.ManyToManyField("CustomInventoryScript", blank=True)
execution_environment = models.ManyToManyField("ExecutionEnvironment", blank=True) execution_environment = models.ManyToManyField("ExecutionEnvironment", blank=True)
notification_template = models.ManyToManyField("NotificationTemplate", 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 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') logger = logging.getLogger('awx.main.models.inventory')
@@ -845,6 +845,7 @@ class InventorySourceOptions(BaseModel):
blank=True, blank=True,
default='', default='',
) )
# TODO: migrate away
source_script = models.ForeignKey( source_script = models.ForeignKey(
'CustomInventoryScript', 'CustomInventoryScript',
null=True, null=True,

View File

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

View File

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

View File

@@ -18,7 +18,6 @@ EXPECTED_VALUES = {
'awx_hosts_total': 1.0, 'awx_hosts_total': 1.0,
'awx_hosts_total': 1.0, 'awx_hosts_total': 1.0,
'awx_schedules_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, '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.Team(organization=objs.organization).save()
models.Host(inventory=jt.inventory).save() models.Host(inventory=jt.inventory).save()
models.Schedule(rrule='DTSTART;TZID=America/New_York:20300504T150000', unified_job_template=jt.job_template).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() output = metrics()
gauges = text_string_to_metric_families(output.decode('UTF-8')) 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 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 @pytest.mark.django_db
def test_list_cannot_order_by_unsearchable_field(get, organization, alice, order_by): def test_list_cannot_order_by_unsearchable_field(get, organization, alice, order_by):
for i, script in enumerate(('#!/bin/a', '#!/bin/b', '#!/bin/c')): get(reverse('api:jobs_list'), alice, QUERY_STRING='order_by=%s' % order_by, expect=403)
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)
@pytest.mark.parametrize("role_field,expected_status_code", [(None, 403), ('admin_role', 201), ('update_role', 403), ('adhoc_role', 403), ('use_role', 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.credential import CredentialType, Credential
from awx.main.models.jobs import JobTemplate, SystemJobTemplate 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 ( from awx.main.models.organization import (
Organization, Organization,
Team, Team,
@@ -540,11 +540,6 @@ def inventory_update(inventory_source):
return InventoryUpdate.objects.create(inventory_source=inventory_source, source=inventory_source.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 @pytest.fixture
def host(group, inventory): def host(group, inventory):
return group.hosts.create(name='single-host', inventory=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( assert decrypt_field(notification_template_with_encrypt, 'notification_configuration', 'token') == decrypt_field(
notification_template_copy, 'notification_configuration', 'token' 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.middleware import URLModificationMiddleware
from awx.main.models import ( # noqa from awx.main.models import ( # noqa
Credential, Credential,
CustomInventoryScript,
Group, Group,
Host, Host,
Instance, Instance,
@@ -204,19 +203,6 @@ def test_inventory_source(get, admin_user):
assert response.data['related']['named_url'].endswith('/test_source++/') 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 @pytest.mark.django_db
def test_credential(get, admin_user, credentialtype_ssh): def test_credential(get, admin_user, credentialtype_ssh):
test_cred = Credential.objects.create(name='test_cred', credential_type=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 ( from awx.main.models import (
Host, Host,
CustomInventoryScript,
Schedule, Schedule,
) )
from awx.main.access import ( from awx.main.access import (
@@ -10,56 +9,10 @@ from awx.main.access import (
InventorySourceAccess, InventorySourceAccess,
HostAccess, HostAccess,
InventoryUpdateAccess, InventoryUpdateAccess,
CustomInventoryScriptAccess,
ScheduleAccess, 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.django_db
@pytest.mark.parametrize("role", ["admin_role", "inventory_admin_role"]) @pytest.mark.parametrize("role", ["admin_role", "inventory_admin_role"])
def test_access_admin(role, organization, inventory, user): def test_access_admin(role, organization, inventory, user):

View File

@@ -1,25 +1,12 @@
# Python # Python
import pytest import pytest
from unittest import mock from unittest import mock
from unittest.mock import PropertyMock
# AWX # AWX
from awx.api.serializers import ( from awx.api.serializers import (
CustomInventoryScriptSerializer,
InventorySourceSerializer, InventorySourceSerializer,
) )
from awx.main.models import ( from awx.main.models import InventorySource
CustomInventoryScript,
InventorySource,
User,
)
# DRF
from rest_framework.request import Request
from rest_framework.test import (
APIRequestFactory,
force_authenticate,
)
@pytest.fixture @pytest.fixture
@@ -30,34 +17,6 @@ def inventory_source(mocker):
return obj 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.UnifiedJobTemplateSerializer.get_related', lambda x, y: {})
@mock.patch('awx.api.serializers.InventorySourceOptionsSerializer.get_related', lambda x, y: {}) @mock.patch('awx.api.serializers.InventorySourceOptionsSerializer.get_related', lambda x, y: {})
class TestInventorySourceSerializerGetRelated(object): 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 ( from awx.main.models import (
AdHocCommand, AdHocCommand,
ActivityStream, ActivityStream,
CustomInventoryScript,
Credential, Credential,
Job, Job,
JobTemplate, JobTemplate,
@@ -106,7 +105,6 @@ def test_filter_on_password_field(password_field, lookup_suffix):
(WorkflowJob, 'survey_passwords__icontains'), (WorkflowJob, 'survey_passwords__icontains'),
(JobTemplate, 'survey_spec__icontains'), (JobTemplate, 'survey_spec__icontains'),
(WorkflowJobTemplate, 'survey_spec__icontains'), (WorkflowJobTemplate, 'survey_spec__icontains'),
(CustomInventoryScript, 'script__icontains'),
(ActivityStream, 'o_auth2_application__client_secret__gt'), (ActivityStream, 'o_auth2_application__client_secret__gt'),
(OAuth2Application, 'grant__code__gt'), (OAuth2Application, 'grant__code__gt'),
], ],

View File

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

View File

@@ -30,7 +30,6 @@ from awx.main.models import (
ProjectUpdate, ProjectUpdate,
UnifiedJob, UnifiedJob,
User, User,
CustomInventoryScript,
build_safe_env, build_safe_env,
) )
from awx.main.models.credential import ManagedCredentialType from awx.main.models.credential import ManagedCredentialType
@@ -1631,55 +1630,6 @@ class TestInventoryUpdateCredentials(TestJobExecution):
assert 'AWS_ACCESS_KEY_ID' not in env assert 'AWS_ACCESS_KEY_ID' not in env
assert 'AWS_SECRET_ACCESS_KEY' 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): def test_ec2_source(self, private_data_dir, inventory_update, mocker):
task = tasks.RunInventoryUpdate() task = tasks.RunInventoryUpdate()
task.instance = inventory_update 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) inventory_source_fields['execution_environment'] = module.resolve_name_to_id('execution_environments', ee)
if source_project is not None: if source_project is not None:
inventory_source_fields['source_project'] = module.resolve_name_to_id('projects', source_project) 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 = ( OPTIONAL_VARS = (
'description', 'description',

View File

@@ -66,12 +66,6 @@ options:
default: [] default: []
type: list type: list
elements: str elements: str
inventory_script:
description:
- List of inventory script names to export
default: []
type: list
elements: str
inventory: inventory:
description: description:
- List of inventory names to export - List of inventory names to export
@@ -153,7 +147,6 @@ def main():
credential=dict(type='list', default=[], elements='str'), credential=dict(type='list', default=[], elements='str'),
credential_type=dict(type='list', default=[], elements='str'), credential_type=dict(type='list', default=[], elements='str'),
inventory=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'), job_template=dict(type='list', default=[], elements='str'),
notification_template=dict(type='list', default=[], elements='str'), notification_template=dict(type='list', default=[], elements='str'),
organization=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 # 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 # 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. # 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 = { needs_param_development = {
'tower_host': ['instance_id'], 'tower_host': ['instance_id'],
} }

View File

@@ -125,64 +125,6 @@ class Inventories(page.PageList, Inventory):
page.register_page([resources.inventories, resources.related_inventories], Inventories) 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): class Group(HasCreate, HasVariables, base.Base):
dependencies = [Inventory] dependencies = [Inventory]
@@ -408,8 +350,6 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
if credential: if credential:
credential = self.ds.credential credential = self.ds.credential
if source_script:
source_script = self.ds.inventory_script
if project: if project:
project = self.ds.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_root_groups = r'inventories/\d+/root_groups/'
_inventory_related_script = r'inventories/\d+/script/' _inventory_related_script = r'inventories/\d+/script/'
_inventory_related_update_inventory_sources = r'inventories/\d+/update_inventory_sources/' _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 = r'inventory_sources/\d+/'
_inventory_source_schedule = r'inventory_sources/\d+/schedules/\d+/' _inventory_source_schedule = r'inventory_sources/\d+/schedules/\d+/'
_inventory_source_schedules = r'inventory_sources/\d+/schedules/' _inventory_source_schedules = r'inventory_sources/\d+/schedules/'

View File

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

View File

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

View File

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