Merge pull request #9822 from AlanCoding/boom_no_scripts

Remove custom inventory scripts from the API

SUMMARY
Connect #7775
AWX VERSION
18.0.0

Reviewed-by: Alan Rominger <arominge@redhat.com>
Reviewed-by: Jeff Bradberry <None>
Reviewed-by: Bianca Henderson <beeankha@gmail.com>
This commit is contained in:
softwarefactory-project-zuul[bot]
2021-04-22 13:17:15 +00:00
committed by GitHub
44 changed files with 251 additions and 599 deletions

View File

@@ -43,7 +43,7 @@ from polymorphic.models import PolymorphicModel
# AWX # AWX
from awx.main.access import get_user_capabilities from awx.main.access import get_user_capabilities
from awx.main.constants import SCHEDULEABLE_PROVIDERS, ACTIVE_STATES, CENSOR_VALUE from awx.main.constants import ACTIVE_STATES, CENSOR_VALUE
from awx.main.models import ( from awx.main.models import (
ActivityStream, ActivityStream,
AdHocCommand, AdHocCommand,
@@ -51,7 +51,6 @@ from awx.main.models import (
Credential, Credential,
CredentialInputSource, CredentialInputSource,
CredentialType, CredentialType,
CustomInventoryScript,
ExecutionEnvironment, ExecutionEnvironment,
Group, Group,
Host, Host,
@@ -92,6 +91,7 @@ from awx.main.models import (
WorkflowJobTemplate, WorkflowJobTemplate,
WorkflowJobTemplateNode, WorkflowJobTemplateNode,
StdoutMaxBytesExceeded, StdoutMaxBytesExceeded,
CLOUD_INVENTORY_SOURCES,
) )
from awx.main.models.base import VERBOSITY_CHOICES, NEW_JOB_TYPE_CHOICES from awx.main.models.base import VERBOSITY_CHOICES, NEW_JOB_TYPE_CHOICES
from awx.main.models.rbac import get_roles_on_resource, role_summary_fields_generator from awx.main.models.rbac import get_roles_on_resource, role_summary_fields_generator
@@ -167,8 +167,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,
'role': ('id', 'role_field'), 'role': ('id', 'role_field'),
'notification_template': DEFAULT_SUMMARY_FIELDS, 'notification_template': DEFAULT_SUMMARY_FIELDS,
'instance_group': ('id', 'name', 'controller_id', 'is_container_group'), 'instance_group': ('id', 'name', 'controller_id', 'is_container_group'),
@@ -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.'))
@@ -2035,7 +1990,6 @@ class InventorySourceOptionsSerializer(BaseSerializer):
'*', '*',
'source', 'source',
'source_path', 'source_path',
'source_script',
'source_vars', 'source_vars',
'credential', 'credential',
'enabled_var', 'enabled_var',
@@ -2053,8 +2007,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):
@@ -2064,34 +2016,6 @@ class InventorySourceOptionsSerializer(BaseSerializer):
raise serializers.ValidationError(_("`{}` is a prohibited environment variable".format(env_k))) raise serializers.ValidationError(_("`{}` is a prohibited environment variable".format(env_k)))
return ret return ret
def validate(self, attrs):
# TODO: Validate source
errors = {}
source = attrs.get('source', self.instance and self.instance.source or '')
source_script = attrs.get('source_script', self.instance and self.instance.source_script or '')
if source == 'custom':
if source_script is None or source_script == '':
errors['source_script'] = _("If 'source' is 'custom', 'source_script' must be provided.")
else:
try:
if not self.instance:
dest_inventory = attrs.get('inventory', None)
if not dest_inventory:
errors['inventory'] = _("Must provide an inventory.")
else:
dest_inventory = self.instance.inventory
if dest_inventory and source_script.organization != dest_inventory.organization:
errors['source_script'] = _("The 'source_script' does not belong to the same organization as the inventory.")
except Exception:
errors['source_script'] = _("'source_script' doesn't exist.")
logger.exception('Problem processing source_script validation.')
if errors:
raise serializers.ValidationError(errors)
return super(InventorySourceOptionsSerializer, self).validate(attrs)
# TODO: remove when old 'credential' fields are removed # TODO: remove when old 'credential' fields are removed
def get_summary_fields(self, obj): def get_summary_fields(self, obj):
summary_fields = super(InventorySourceOptionsSerializer, self).get_summary_fields(obj) summary_fields = super(InventorySourceOptionsSerializer, self).get_summary_fields(obj)
@@ -4808,7 +4732,7 @@ class ScheduleSerializer(LaunchConfigurationBaseSerializer, SchedulePreviewSeria
return summary_fields return summary_fields
def validate_unified_job_template(self, value): def validate_unified_job_template(self, value):
if type(value) == InventorySource and value.source not in SCHEDULEABLE_PROVIDERS: if type(value) == InventorySource and value.source not in CLOUD_INVENTORY_SOURCES:
raise serializers.ValidationError(_('Inventory Source must be a cloud resource.')) raise serializers.ValidationError(_('Inventory Source must be a cloud resource.'))
elif type(value) == Project and value.scm_type == '': elif type(value) == Project and value.scm_type == '':
raise serializers.ValidationError(_('Manual Project cannot have a schedule set.')) raise serializers.ValidationError(_('Manual Project cannot have a schedule set.'))

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)):
@@ -1031,7 +1030,7 @@ class InventorySourceAccess(NotificationAttachMixin, BaseAccess):
model = InventorySource model = InventorySource
select_related = ('created_by', 'modified_by', 'inventory') select_related = ('created_by', 'modified_by', 'inventory')
prefetch_related = ('credentials__credential_type', 'last_job', 'source_script', 'source_project') prefetch_related = ('credentials__credential_type', 'last_job', 'source_project')
def filtered_queryset(self): def filtered_queryset(self):
return self.model.objects.filter(inventory__in=Inventory.accessible_pk_qs(self.user, 'read_role')) return self.model.objects.filter(inventory__in=Inventory.accessible_pk_qs(self.user, 'read_role'))
@@ -1093,7 +1092,7 @@ class InventoryUpdateAccess(BaseAccess):
'modified_by', 'modified_by',
'inventory_source', 'inventory_source',
) )
prefetch_related = ('unified_job_template', 'instance_group', 'credentials__credential_type', 'inventory', 'source_script') prefetch_related = ('unified_job_template', 'instance_group', 'credentials__credential_type', 'inventory')
def filtered_queryset(self): def filtered_queryset(self):
return self.model.objects.filter(inventory_source__inventory__in=Inventory.accessible_pk_qs(self.user, 'read_role')) return self.model.objects.filter(inventory_source__inventory__in=Inventory.accessible_pk_qs(self.user, 'read_role'))
@@ -2671,7 +2670,6 @@ class ActivityStreamAccess(BaseAccess):
'role', 'role',
'actor', 'actor',
'schedule', 'schedule',
'custom_inventory_script',
'unified_job_template', 'unified_job_template',
'workflow_job_template_node', 'workflow_job_template_node',
) )
@@ -2755,33 +2753,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

@@ -31,7 +31,6 @@ def metrics():
registry=REGISTRY, registry=REGISTRY,
) )
SCHEDULE_COUNT = Gauge('awx_schedules_total', 'Number of schedules', registry=REGISTRY) SCHEDULE_COUNT = Gauge('awx_schedules_total', 'Number of schedules', registry=REGISTRY)
INV_SCRIPT_COUNT = Gauge('awx_inventory_scripts_total', 'Number of invetory scripts', registry=REGISTRY)
USER_SESSIONS = Gauge( USER_SESSIONS = Gauge(
'awx_sessions_total', 'awx_sessions_total',
'Number of sessions', 'Number of sessions',
@@ -160,7 +159,6 @@ def metrics():
HOST_COUNT.labels(type='active').set(current_counts['active_host_count']) HOST_COUNT.labels(type='active').set(current_counts['active_host_count'])
SCHEDULE_COUNT.set(current_counts['schedule']) SCHEDULE_COUNT.set(current_counts['schedule'])
INV_SCRIPT_COUNT.set(current_counts['custom_inventory_script'])
CUSTOM_VENVS.set(current_counts['custom_virtualenvs']) CUSTOM_VENVS.set(current_counts['custom_virtualenvs'])
USER_SESSIONS.labels(type='all').set(current_counts['active_sessions']) USER_SESSIONS.labels(type='all').set(current_counts['active_sessions'])

View File

@@ -235,11 +235,7 @@ register(
'AWX_ISOLATION_BASE_PATH', 'AWX_ISOLATION_BASE_PATH',
field_class=fields.CharField, field_class=fields.CharField,
label=_('Job execution path'), label=_('Job execution path'),
help_text=_( help_text=_('The directory in which Tower will create new temporary directories for job execution and isolation (such as credential files).'),
'The directory in which Tower will create new temporary '
'directories for job execution and isolation '
'(such as credential files and custom inventory scripts).'
),
category=_('Jobs'), category=_('Jobs'),
category_slug='jobs', category_slug='jobs',
) )

View File

@@ -7,7 +7,6 @@ from django.utils.translation import ugettext_lazy as _
__all__ = [ __all__ = [
'CLOUD_PROVIDERS', 'CLOUD_PROVIDERS',
'SCHEDULEABLE_PROVIDERS',
'PRIVILEGE_ESCALATION_METHODS', 'PRIVILEGE_ESCALATION_METHODS',
'ANSI_SGR_PATTERN', 'ANSI_SGR_PATTERN',
'CAN_CANCEL', 'CAN_CANCEL',
@@ -16,10 +15,6 @@ __all__ = [
] ]
CLOUD_PROVIDERS = ('azure_rm', 'ec2', 'gce', 'vmware', 'openstack', 'rhv', 'satellite6', 'tower') CLOUD_PROVIDERS = ('azure_rm', 'ec2', 'gce', 'vmware', 'openstack', 'rhv', 'satellite6', 'tower')
SCHEDULEABLE_PROVIDERS = CLOUD_PROVIDERS + (
'custom',
'scm',
)
PRIVILEGE_ESCALATION_METHODS = [ PRIVILEGE_ESCALATION_METHODS = [
('sudo', _('Sudo')), ('sudo', _('Sudo')),
('su', _('Su')), ('su', _('Su')),

View File

@@ -0,0 +1,36 @@
import tempfile
import tarfile
import stat
import os
from awx.main.models.inventory import CustomInventoryScript
from django.core.management.base import BaseCommand
from django.utils.text import slugify
class Command(BaseCommand):
help = 'Export custom inventory scripts into a tarfile.'
def add_arguments(self, parser):
parser.add_argument('--filename', dest='filename', type=str, default='custom_scripts.tar', help='Filename of the output tar file')
def handle(self, **options):
tar_filename = options.get('filename')
with tempfile.TemporaryDirectory() as tmpdirname:
with tarfile.open(tar_filename, "w") as tar:
for cis in CustomInventoryScript.objects.all():
# naming convention similar to project paths
slug_name = slugify(str(cis.name)).replace(u'-', u'_')
script_filename = u'_%d__%s' % (int(cis.pk), slug_name)
script_path = os.path.join(tmpdirname, script_filename)
with open(script_path, 'w') as f:
f.write(cis.script)
os.chmod(script_path, stat.S_IRWXU)
tar.add(script_path, arcname=script_filename)
print('Dump of old custom inventory scripts at {}'.format(tar_filename))

View File

@@ -147,9 +147,6 @@ class Command(BaseCommand):
parser.add_argument('--overwrite', dest='overwrite', action='store_true', default=False, help='overwrite the destination hosts and groups') parser.add_argument('--overwrite', dest='overwrite', action='store_true', default=False, help='overwrite the destination hosts and groups')
parser.add_argument('--overwrite-vars', dest='overwrite_vars', action='store_true', default=False, help='overwrite (rather than merge) variables') parser.add_argument('--overwrite-vars', dest='overwrite_vars', action='store_true', default=False, help='overwrite (rather than merge) variables')
parser.add_argument('--keep-vars', dest='keep_vars', action='store_true', default=False, help='DEPRECATED legacy option, has no effect') parser.add_argument('--keep-vars', dest='keep_vars', action='store_true', default=False, help='DEPRECATED legacy option, has no effect')
parser.add_argument(
'--custom', dest='custom', action='store_true', default=False, help='DEPRECATED indicates a custom inventory script, no longer used'
)
parser.add_argument('--source', dest='source', type=str, default=None, metavar='s', help='inventory directory, file, or script to load') parser.add_argument('--source', dest='source', type=str, default=None, metavar='s', help='inventory directory, file, or script to load')
parser.add_argument( parser.add_argument(
'--enabled-var', '--enabled-var',

View File

@@ -0,0 +1,17 @@
# Generated by Django 2.2.16 on 2021-04-13 19:51
from django.db import migrations
# AWX migration utils
from awx.main.migrations._inventory_source import delete_custom_inv_source
class Migration(migrations.Migration):
dependencies = [
('main', '0136_scm_track_submodules'),
]
operations = [
migrations.RunPython(delete_custom_inv_source),
]

View File

@@ -0,0 +1,84 @@
# Generated by Django 2.2.16 on 2021-04-13 19:51
from django.db import migrations, models
from awx.main.migrations._rbac import delete_all_custom_script_roles
class Migration(migrations.Migration):
dependencies = [
('main', '0137_custom_inventory_scripts_removal_data'),
]
operations = [
migrations.RemoveField(
model_name='activitystream',
name='custom_inventory_script',
),
migrations.RemoveField(
model_name='inventorysource',
name='source_script',
),
migrations.RemoveField(
model_name='inventoryupdate',
name='source_script',
),
migrations.AlterField(
model_name='inventorysource',
name='source',
field=models.CharField(
choices=[
('file', 'File, Directory or Script'),
('scm', 'Sourced from a Project'),
('ec2', 'Amazon EC2'),
('gce', 'Google Compute Engine'),
('azure_rm', 'Microsoft Azure Resource Manager'),
('vmware', 'VMware vCenter'),
('satellite6', 'Red Hat Satellite 6'),
('openstack', 'OpenStack'),
('rhv', 'Red Hat Virtualization'),
('tower', 'Ansible Tower'),
],
default=None,
max_length=32,
),
),
migrations.AlterField(
model_name='inventoryupdate',
name='source',
field=models.CharField(
choices=[
('file', 'File, Directory or Script'),
('scm', 'Sourced from a Project'),
('ec2', 'Amazon EC2'),
('gce', 'Google Compute Engine'),
('azure_rm', 'Microsoft Azure Resource Manager'),
('vmware', 'VMware vCenter'),
('satellite6', 'Red Hat Satellite 6'),
('openstack', 'OpenStack'),
('rhv', 'Red Hat Virtualization'),
('tower', 'Ansible Tower'),
],
default=None,
max_length=32,
),
),
migrations.AlterUniqueTogether(
name='custominventoryscript',
unique_together=set(),
),
migrations.RemoveField(
model_name='custominventoryscript',
name='admin_role',
),
migrations.RemoveField(
model_name='custominventoryscript',
name='organization',
),
migrations.RemoveField(
model_name='custominventoryscript',
name='read_role',
),
migrations.RunPython(delete_all_custom_script_roles),
]

View File

@@ -90,3 +90,22 @@ def delete_cloudforms_inv_source(apps, schema_editor):
if ct: if ct:
ct.credentials.all().delete() ct.credentials.all().delete()
ct.delete() ct.delete()
def delete_custom_inv_source(apps, schema_editor):
set_current_apps(apps)
InventorySource = apps.get_model('main', 'InventorySource')
InventoryUpdate = apps.get_model('main', 'InventoryUpdate')
ct, deletions = InventoryUpdate.objects.filter(source='custom').delete()
if ct:
logger.info('deleted {}'.format((ct, deletions)))
update_ct = deletions['main.InventoryUpdate']
if update_ct:
logger.info('Deleted {} custom inventory script sources.'.format(update_ct))
ct, deletions = InventorySource.objects.filter(source='custom').delete()
if ct:
logger.info('deleted {}'.format((ct, deletions)))
src_ct = deletions['main.InventorySource']
if src_ct:
logger.info('Deleted {} custom inventory script updates.'.format(src_ct))
logger.warning('Custom inventory scripts have been removed, see awx-manage export_custom_scripts')

View File

@@ -28,7 +28,6 @@ def create_roles(apps, schema_editor):
'Inventory', 'Inventory',
'Project', 'Project',
'Credential', 'Credential',
'CustomInventoryScript',
'JobTemplate', 'JobTemplate',
] ]
] ]
@@ -48,6 +47,21 @@ def delete_all_user_roles(apps, schema_editor):
role.delete() role.delete()
def delete_all_custom_script_roles(apps, schema_editor):
ContentType = apps.get_model('contenttypes', "ContentType")
Role = apps.get_model('main', "Role")
try:
cis_type = ContentType.objects.get(model='custominventoryscript')
except ContentType.DoesNotExist:
return
role_ct = 0
for role in Role.objects.filter(content_type=cis_type).iterator():
role.delete()
role_ct += 1
if role_ct:
logger.debug('Deleted {} roles corresponding to custom inventory sources.'.format(role_ct))
UNIFIED_ORG_LOOKUPS = { UNIFIED_ORG_LOOKUPS = {
# Job Templates had an implicit organization via their project # Job Templates had an implicit organization via their project
'jobtemplate': 'project', 'jobtemplate': 'project',

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,7 +74,6 @@ 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)
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)
notification = models.ManyToManyField("Notification", blank=True) notification = models.ManyToManyField("Notification", blank=True)

View File

@@ -62,7 +62,7 @@ PROJECT_UPDATE_JOB_TYPE_CHOICES = [
(PERM_INVENTORY_CHECK, _('Check')), (PERM_INVENTORY_CHECK, _('Check')),
] ]
CLOUD_INVENTORY_SOURCES = list(CLOUD_PROVIDERS) + ['scm', 'custom'] CLOUD_INVENTORY_SOURCES = list(CLOUD_PROVIDERS) + ['scm']
VERBOSITY_CHOICES = [ VERBOSITY_CHOICES = [
(0, '0 (Normal)'), (0, '0 (Normal)'),

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')
@@ -821,7 +821,6 @@ class InventorySourceOptions(BaseModel):
('openstack', _('OpenStack')), ('openstack', _('OpenStack')),
('rhv', _('Red Hat Virtualization')), ('rhv', _('Red Hat Virtualization')),
('tower', _('Ansible Tower')), ('tower', _('Ansible Tower')),
('custom', _('Custom Script')),
] ]
# From the options of the Django management base command # From the options of the Django management base command
@@ -845,13 +844,6 @@ class InventorySourceOptions(BaseModel):
blank=True, blank=True,
default='', default='',
) )
source_script = models.ForeignKey(
'CustomInventoryScript',
null=True,
default=None,
blank=True,
on_delete=models.SET_NULL,
)
source_vars = models.TextField( source_vars = models.TextField(
blank=True, blank=True,
default='', default='',
@@ -1328,7 +1320,6 @@ class InventoryUpdate(UnifiedJob, InventorySourceOptions, JobNotificationMixin,
class CustomInventoryScript(CommonModelNameNotUnique, ResourceMixin): class CustomInventoryScript(CommonModelNameNotUnique, ResourceMixin):
class Meta: class Meta:
app_label = 'main' app_label = 'main'
unique_together = [('name', 'organization')]
ordering = ('name',) ordering = ('name',)
script = prevent_search( script = prevent_search(
@@ -1338,21 +1329,6 @@ class CustomInventoryScript(CommonModelNameNotUnique, ResourceMixin):
help_text=_('Inventory script contents'), help_text=_('Inventory script contents'),
) )
) )
organization = models.ForeignKey(
'Organization',
related_name='custom_inventory_scripts',
help_text=_('Organization owning this inventory script'),
blank=False,
null=True,
on_delete=models.SET_NULL,
)
admin_role = ImplicitRoleField(
parent_role='organization.admin_role',
)
read_role = ImplicitRoleField(
parent_role=['organization.auditor_role', 'organization.member_role', 'admin_role'],
)
def get_absolute_url(self, request=None): def get_absolute_url(self, request=None):
return reverse('api:inventory_script_detail', kwargs={'pk': self.pk}, request=request) return reverse('api:inventory_script_detail', kwargs={'pk': self.pk}, request=request)

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

@@ -2412,7 +2412,7 @@ class RunInventoryUpdate(BaseTask):
# All CLOUD_PROVIDERS sources implement as inventory plugin from collection # All CLOUD_PROVIDERS sources implement as inventory plugin from collection
env['ANSIBLE_INVENTORY_ENABLED'] = 'auto' env['ANSIBLE_INVENTORY_ENABLED'] = 'auto'
if inventory_update.source in ['scm', 'custom']: if inventory_update.source == 'scm':
for env_k in inventory_update.source_vars_dict: for env_k in inventory_update.source_vars_dict:
if str(env_k) not in env and str(env_k) not in settings.INV_ENV_VARIABLE_BLOCKED: if str(env_k) not in env and str(env_k) not in settings.INV_ENV_VARIABLE_BLOCKED:
env[str(env_k)] = str(inventory_update.source_vars_dict[env_k]) env[str(env_k)] = str(inventory_update.source_vars_dict[env_k])
@@ -2513,16 +2513,7 @@ class RunInventoryUpdate(BaseTask):
rel_path = injector.filename rel_path = injector.filename
elif src == 'scm': elif src == 'scm':
rel_path = os.path.join('project', inventory_update.source_path) rel_path = os.path.join('project', inventory_update.source_path)
elif src == 'custom':
handle, inventory_path = tempfile.mkstemp(dir=private_data_dir)
f = os.fdopen(handle, 'w')
if inventory_update.source_script is None:
raise RuntimeError('Inventory Script does not exist')
f.write(inventory_update.source_script.script)
f.close()
os.chmod(inventory_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
rel_path = os.path.split(inventory_path)[-1]
return rel_path return rel_path
def build_cwd(self, inventory_update, private_data_dir): def build_cwd(self, inventory_update, private_data_dir):

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:job_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

@@ -174,7 +174,7 @@ class TestSCMClean:
@pytest.mark.django_db @pytest.mark.django_db
class TestInventorySourceInjectors: class TestInventorySourceInjectors:
def test_extra_credentials(self, project, credential): def test_extra_credentials(self, project, credential):
inventory_source = InventorySource.objects.create(name='foo', source='custom', source_project=project) inventory_source = InventorySource.objects.create(name='foo', source='scm', source_project=project)
inventory_source.credentials.add(credential) inventory_source.credentials.add(credential)
assert inventory_source.get_cloud_credential() == credential # for serializer assert inventory_source.get_cloud_credential() == credential # for serializer
assert inventory_source.get_extra_credentials() == [credential] assert inventory_source.get_extra_credentials() == [credential]

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

@@ -198,7 +198,7 @@ class TestWFJTCopyAccess:
def test_workflow_copy_no_start(self, wfjt, inventory, admin_user): def test_workflow_copy_no_start(self, wfjt, inventory, admin_user):
# Test that un-startable resource doesn't block copy # Test that un-startable resource doesn't block copy
inv_src = InventorySource.objects.create(inventory=inventory, source='custom', source_script=None) inv_src = InventorySource.objects.create(inventory=inventory, source='file')
assert not inv_src.can_update assert not inv_src.can_update
wfjt.workflow_job_template_nodes.create(unified_job_template=inv_src) wfjt.workflow_job_template_nodes.create(unified_job_template=inv_src)
access = WorkflowJobTemplateAccess(admin_user, save_messages=True) access = WorkflowJobTemplateAccess(admin_user, save_messages=True)

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

@@ -48,10 +48,6 @@ options:
description: description:
- For an SCM based inventory source, the source path points to the file within the repo to use as an inventory. - For an SCM based inventory source, the source path points to the file within the repo to use as an inventory.
type: str type: str
source_script:
description:
- Inventory script to be used when group type is C(custom).
type: str
source_vars: source_vars:
description: description:
- The variables or environment fields to apply to this source type. - The variables or environment fields to apply to this source type.
@@ -164,7 +160,6 @@ def main():
# #
source=dict(choices=["scm", "ec2", "gce", "azure_rm", "vmware", "satellite6", "openstack", "rhv", "tower", "custom"]), source=dict(choices=["scm", "ec2", "gce", "azure_rm", "vmware", "satellite6", "openstack", "rhv", "tower", "custom"]),
source_path=dict(), source_path=dict(),
source_script=dict(),
source_vars=dict(type='dict'), source_vars=dict(type='dict'),
enabled_var=dict(), enabled_var=dict(),
enabled_value=dict(), enabled_value=dict(),
@@ -194,7 +189,6 @@ def main():
new_name = module.params.get('new_name') new_name = module.params.get('new_name')
inventory = module.params.get('inventory') inventory = module.params.get('inventory')
organization = module.params.get('organization') organization = module.params.get('organization')
source_script = module.params.get('source_script')
credential = module.params.get('credential') credential = module.params.get('credential')
ee = module.params.get('execution_environment') ee = module.params.get('execution_environment')
source_project = module.params.get('source_project') source_project = module.params.get('source_project')
@@ -256,8 +250,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,68 +125,10 @@ 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]
optional_dependencies = [Credential, InventoryScript] optional_dependencies = [Credential]
NATURAL_KEY = ('name', 'inventory') NATURAL_KEY = ('name', 'inventory')
@property @property
@@ -215,9 +157,8 @@ class Group(HasCreate, HasVariables, base.Base):
return payload return payload
def create_payload(self, name='', description='', inventory=Inventory, credential=None, source_script=None, **kwargs): def create_payload(self, name='', description='', inventory=Inventory, credential=None, **kwargs):
credential, source_script = filter_by_class((credential, Credential), (source_script, InventoryScript)) self.create_and_update_dependencies(inventory, credential)
self.create_and_update_dependencies(inventory, credential, source_script)
credential = self.ds.credential if credential else None credential = self.ds.credential if credential else None
payload = self.payload(inventory=self.ds.inventory, credential=credential, name=name, description=description, **kwargs) payload = self.payload(inventory=self.ds.inventory, credential=credential, name=name, description=description, **kwargs)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store) payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
@@ -356,10 +297,10 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
optional_schedule_fields = tuple() optional_schedule_fields = tuple()
dependencies = [Inventory] dependencies = [Inventory]
optional_dependencies = [Credential, InventoryScript, Project] optional_dependencies = [Credential, Project]
NATURAL_KEY = ('organization', 'name', 'inventory') NATURAL_KEY = ('organization', 'name', 'inventory')
def payload(self, inventory, source='custom', credential=None, source_script=None, project=None, **kwargs): def payload(self, inventory, source='scm', credential=None, project=None, **kwargs):
payload = PseudoNamespace( payload = PseudoNamespace(
name=kwargs.get('name') or 'InventorySource - {}'.format(random_title()), name=kwargs.get('name') or 'InventorySource - {}'.format(random_title()),
description=kwargs.get('description') or random_title(10), description=kwargs.get('description') or random_title(10),
@@ -369,8 +310,6 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
if credential: if credential:
payload.credential = credential.id payload.credential = credential.id
if source_script:
payload.source_script = source_script.id
if project: if project:
payload.source_project = project.id payload.source_project = project.id
@@ -390,52 +329,27 @@ class InventorySource(HasCreate, HasNotifications, UnifiedJobTemplate):
return payload return payload
def create_payload( def create_payload(self, name='', description='', source='scm', inventory=Inventory, credential=None, project=None, **kwargs):
self, name='', description='', source='custom', inventory=Inventory, credential=None, source_script=InventoryScript, project=None, **kwargs
):
if source != 'custom' and source_script == InventoryScript:
source_script = None
if source == 'scm': if source == 'scm':
kwargs.setdefault('overwrite_vars', True) kwargs.setdefault('overwrite_vars', True)
kwargs.setdefault('source_path', 'inventories/script_migrations/script_source.py')
if project is None: if project is None:
project = Project project = Project
inventory, credential, source_script, project = filter_by_class( inventory, credential, project = filter_by_class((inventory, Inventory), (credential, Credential), (project, Project))
(inventory, Inventory), (credential, Credential), (source_script, InventoryScript), (project, Project) self.create_and_update_dependencies(inventory, credential, project)
)
self.create_and_update_dependencies(inventory, credential, source_script, project)
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
payload = self.payload( payload = self.payload(inventory=self.ds.inventory, source=source, credential=credential, project=project, name=name, description=description, **kwargs)
inventory=self.ds.inventory,
source=source,
credential=credential,
source_script=source_script,
project=project,
name=name,
description=description,
**kwargs
)
payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store) payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
return payload return payload
def create(self, name='', description='', source='custom', inventory=Inventory, credential=None, source_script=InventoryScript, project=None, **kwargs): def create(self, name='', description='', source='scm', inventory=Inventory, credential=None, project=None, **kwargs):
payload = self.create_payload( payload = self.create_payload(name=name, description=description, source=source, inventory=inventory, credential=credential, project=project, **kwargs)
name=name,
description=description,
source=source,
inventory=inventory,
credential=credential,
source_script=source_script,
project=project,
**kwargs
)
return self.update_identity(InventorySources(self.connection).post(payload)) return self.update_identity(InventorySources(self.connection).post(payload))
def update(self): def update(self):

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',