Merge pull request #5985 from AlanCoding/scm_inv5

SCM file-based inventory sources
This commit is contained in:
Alan Rominger
2017-04-17 16:22:24 -04:00
committed by GitHub
18 changed files with 516 additions and 47 deletions

View File

@@ -81,6 +81,7 @@ SUMMARIZABLE_FK_FIELDS = {
'groups_with_active_failures',
'has_inventory_sources'),
'project': DEFAULT_SUMMARY_FIELDS + ('status', 'scm_type'),
'scm_project': DEFAULT_SUMMARY_FIELDS + ('status', 'scm_type'),
'project_update': DEFAULT_SUMMARY_FIELDS + ('status', 'failed',),
'credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud'),
'cloud_credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud'),
@@ -960,8 +961,10 @@ class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer):
res.update(dict(
teams = self.reverse('api:project_teams_list', kwargs={'pk': obj.pk}),
playbooks = self.reverse('api:project_playbooks', kwargs={'pk': obj.pk}),
inventory_files = self.reverse('api:project_inventories', kwargs={'pk': obj.pk}),
update = self.reverse('api:project_update_view', kwargs={'pk': obj.pk}),
project_updates = self.reverse('api:project_updates_list', kwargs={'pk': obj.pk}),
scm_inventories = self.reverse('api:project_scm_inventory_sources', kwargs={'pk': obj.pk}),
schedules = self.reverse('api:project_schedules_list', kwargs={'pk': obj.pk}),
activity_stream = self.reverse('api:project_activity_stream_list', kwargs={'pk': obj.pk}),
notification_templates_any = self.reverse('api:project_notification_templates_any_list', kwargs={'pk': obj.pk}),
@@ -1027,6 +1030,23 @@ class ProjectPlaybooksSerializer(ProjectSerializer):
return ReturnList(ret, serializer=self)
class ProjectInventoriesSerializer(ProjectSerializer):
inventory_files = serializers.ReadOnlyField(help_text=_(
'Array of inventory files and directories available within this project, '
'not comprehensive.'))
class Meta:
model = Project
fields = ('inventory_files',)
@property
def data(self):
ret = super(ProjectInventoriesSerializer, self).data
ret = ret.get('inventory_files', [])
return ReturnList(ret, serializer=self)
class ProjectUpdateViewSerializer(ProjectSerializer):
can_update = serializers.BooleanField(read_only=True)
@@ -1046,6 +1066,7 @@ class ProjectUpdateSerializer(UnifiedJobSerializer, ProjectOptionsSerializer):
res.update(dict(
project = self.reverse('api:project_detail', kwargs={'pk': obj.project.pk}),
cancel = self.reverse('api:project_update_cancel', kwargs={'pk': obj.pk}),
scm_inventory_updates = self.reverse('api:project_update_scm_inventory_updates', kwargs={'pk': obj.pk}),
notifications = self.reverse('api:project_update_notifications_list', kwargs={'pk': obj.pk}),
))
return res
@@ -1481,7 +1502,7 @@ class InventorySourceSerializer(UnifiedJobTemplateSerializer, InventorySourceOpt
class Meta:
model = InventorySource
fields = ('*', 'name', 'inventory', 'update_on_launch', 'update_cache_timeout') + \
fields = ('*', 'name', 'inventory', 'update_on_launch', 'update_cache_timeout', 'scm_project') + \
('last_update_failed', 'last_updated', 'group') # Backwards compatibility.
def get_related(self, obj):
@@ -1499,6 +1520,8 @@ class InventorySourceSerializer(UnifiedJobTemplateSerializer, InventorySourceOpt
))
if obj.inventory:
res['inventory'] = self.reverse('api:inventory_detail', kwargs={'pk': obj.inventory.pk})
if obj.scm_project_id is not None:
res['scm_project'] = self.reverse('api:project_detail', kwargs={'pk': obj.scm_project.pk})
# Backwards compatibility.
if obj.current_update:
res['current_update'] = self.reverse('api:inventory_update_detail',
@@ -1530,6 +1553,14 @@ class InventorySourceSerializer(UnifiedJobTemplateSerializer, InventorySourceOpt
return True
return False
def build_relational_field(self, field_name, relation_info):
field_class, field_kwargs = super(InventorySourceSerializer, self).build_relational_field(field_name, relation_info)
# SCM Project and inventory are read-only unless creating a new inventory.
if self.instance and field_name in ['scm_project', 'inventory']:
field_kwargs['read_only'] = True
field_kwargs.pop('queryset', None)
return field_class, field_kwargs
def to_representation(self, obj):
ret = super(InventorySourceSerializer, self).to_representation(obj)
if obj is None:
@@ -1538,6 +1569,16 @@ class InventorySourceSerializer(UnifiedJobTemplateSerializer, InventorySourceOpt
ret['inventory'] = None
return ret
def validate(self, attrs):
# source_path = attrs.get('source_path', self.instance and self.instance.source_path)
update_on_launch = attrs.get('update_on_launch', self.instance and self.instance.update_on_launch)
scm_project = attrs.get('scm_project', self.instance and self.instance.scm_project)
if attrs.get('source_path', None) and not scm_project:
raise serializers.ValidationError({"detail": _("Cannot set source_path if not SCM type.")})
elif update_on_launch and scm_project:
raise serializers.ValidationError({"detail": _("Cannot update SCM-based inventory source on launch.")})
return super(InventorySourceSerializer, self).validate(attrs)
class InventorySourceUpdateSerializer(InventorySourceSerializer):

View File

@@ -48,6 +48,8 @@ project_urls = patterns('awx.api.views',
url(r'^$', 'project_list'),
url(r'^(?P<pk>[0-9]+)/$', 'project_detail'),
url(r'^(?P<pk>[0-9]+)/playbooks/$', 'project_playbooks'),
url(r'^(?P<pk>[0-9]+)/inventories/$', 'project_inventories'),
url(r'^(?P<pk>[0-9]+)/scm_inventory_sources/$', 'project_scm_inventory_sources'),
url(r'^(?P<pk>[0-9]+)/teams/$', 'project_teams_list'),
url(r'^(?P<pk>[0-9]+)/update/$', 'project_update_view'),
url(r'^(?P<pk>[0-9]+)/project_updates/$', 'project_updates_list'),
@@ -65,6 +67,7 @@ project_update_urls = patterns('awx.api.views',
url(r'^(?P<pk>[0-9]+)/$', 'project_update_detail'),
url(r'^(?P<pk>[0-9]+)/cancel/$', 'project_update_cancel'),
url(r'^(?P<pk>[0-9]+)/stdout/$', 'project_update_stdout'),
url(r'^(?P<pk>[0-9]+)/scm_inventory_updates/$', 'project_update_scm_inventory_updates'),
url(r'^(?P<pk>[0-9]+)/notifications/$', 'project_update_notifications_list'),
)

View File

@@ -1074,6 +1074,12 @@ class ProjectPlaybooks(RetrieveAPIView):
serializer_class = ProjectPlaybooksSerializer
class ProjectInventories(RetrieveAPIView):
model = Project
serializer_class = ProjectInventoriesSerializer
class ProjectTeamsList(ListAPIView):
model = Team
@@ -1101,6 +1107,17 @@ class ProjectSchedulesList(SubListCreateAPIView):
new_in_148 = True
class ProjectScmInventorySources(SubListCreateAPIView):
view_name = _("Project SCM Inventory Sources")
model = Inventory
serializer_class = InventorySourceSerializer
parent_model = Project
relationship = 'scm_inventory_sources'
parent_key = 'scm_project'
new_in_320 = True
class ProjectActivityStreamList(ActivityStreamEnforcementMixin, SubListAPIView):
model = ActivityStream
@@ -1226,6 +1243,17 @@ class ProjectUpdateNotificationsList(SubListAPIView):
new_in_300 = True
class ProjectUpdateScmInventoryUpdates(SubListCreateAPIView):
view_name = _("Project Update SCM Inventory Updates")
model = InventoryUpdate
serializer_class = InventoryUpdateSerializer
parent_model = ProjectUpdate
relationship = 'scm_inventory_updates'
parent_key = 'scm_project_update'
new_in_320 = True
class ProjectAccessList(ResourceAccessList):
model = User # needs to be User for AccessLists's
@@ -2185,7 +2213,7 @@ class InventoryInventorySourcesList(SubListCreateAPIView):
new_in_14 = True
class InventorySourceList(ListAPIView):
class InventorySourceList(ListCreateAPIView):
model = InventorySource
serializer_class = InventorySourceSerializer
@@ -2292,6 +2320,10 @@ class InventorySourceUpdateView(RetrieveAPIView):
def post(self, request, *args, **kwargs):
obj = self.get_object()
if obj.source == 'file' and obj.scm_project_id is not None:
raise PermissionDenied(detail=_(
'Update the project `{}` in order to update this inventory source.'.format(
obj.scm_project.name)))
if obj.can_update:
inventory_update = obj.update()
if not inventory_update: