mirror of
https://github.com/ansible/awx.git
synced 2026-03-10 14:09:28 -02:30
AC-601 Add groups/hosts collections to inventory sources, add inventory sources collections to groups/hosts to indicate which inventory source(s) have created/updated the given groups/hosts. Prevent setting an explicit inventory source for a group that is being managed by another parent with cloud inventory source.
This commit is contained in:
@@ -722,7 +722,7 @@ class HostSerializer(BaseSerializerWithVariables):
|
|||||||
job_events = reverse('api:host_job_events_list', args=(obj.pk,)),
|
job_events = reverse('api:host_job_events_list', args=(obj.pk,)),
|
||||||
job_host_summaries = reverse('api:host_job_host_summaries_list', args=(obj.pk,)),
|
job_host_summaries = reverse('api:host_job_host_summaries_list', args=(obj.pk,)),
|
||||||
activity_stream = reverse('api:host_activity_stream_list', args=(obj.pk,)),
|
activity_stream = reverse('api:host_activity_stream_list', args=(obj.pk,)),
|
||||||
#inventory_sources = reverse('api:host_inventory_sources_list', args=(obj.pk,)),
|
inventory_sources = reverse('api:host_inventory_sources_list', args=(obj.pk,)),
|
||||||
))
|
))
|
||||||
if obj.inventory and obj.inventory.active:
|
if obj.inventory and obj.inventory.active:
|
||||||
res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,))
|
res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,))
|
||||||
@@ -833,7 +833,7 @@ class GroupSerializer(BaseSerializerWithVariables):
|
|||||||
job_events = reverse('api:group_job_events_list', args=(obj.pk,)),
|
job_events = reverse('api:group_job_events_list', args=(obj.pk,)),
|
||||||
job_host_summaries = reverse('api:group_job_host_summaries_list', args=(obj.pk,)),
|
job_host_summaries = reverse('api:group_job_host_summaries_list', args=(obj.pk,)),
|
||||||
activity_stream = reverse('api:group_activity_stream_list', args=(obj.pk,)),
|
activity_stream = reverse('api:group_activity_stream_list', args=(obj.pk,)),
|
||||||
#inventory_sources = reverse('api:group_inventory_sources_list', args=(obj.pk,)),
|
inventory_sources = reverse('api:group_inventory_sources_list', args=(obj.pk,)),
|
||||||
))
|
))
|
||||||
if obj.inventory and obj.inventory.active:
|
if obj.inventory and obj.inventory.active:
|
||||||
res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,))
|
res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,))
|
||||||
@@ -981,8 +981,8 @@ class InventorySourceSerializer(UnifiedJobTemplateSerializer, InventorySourceOpt
|
|||||||
inventory_updates = reverse('api:inventory_source_updates_list', args=(obj.pk,)),
|
inventory_updates = reverse('api:inventory_source_updates_list', args=(obj.pk,)),
|
||||||
schedules = reverse('api:inventory_source_schedules_list', args=(obj.pk,)),
|
schedules = reverse('api:inventory_source_schedules_list', args=(obj.pk,)),
|
||||||
activity_stream = reverse('api:inventory_activity_stream_list', args=(obj.pk,)),
|
activity_stream = reverse('api:inventory_activity_stream_list', args=(obj.pk,)),
|
||||||
#hosts = reverse('api:inventory_source_hosts_list', args=(obj.pk,)),
|
hosts = reverse('api:inventory_source_hosts_list', args=(obj.pk,)),
|
||||||
#groups = reverse('api:inventory_source_groups_list', args=(obj.pk,)),
|
groups = reverse('api:inventory_source_groups_list', args=(obj.pk,)),
|
||||||
))
|
))
|
||||||
if obj.inventory and obj.inventory.active:
|
if obj.inventory and obj.inventory.active:
|
||||||
res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,))
|
res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,))
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ host_urls = patterns('awx.api.views',
|
|||||||
url(r'^(?P<pk>[0-9]+)/job_events/', 'host_job_events_list'),
|
url(r'^(?P<pk>[0-9]+)/job_events/', 'host_job_events_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/job_host_summaries/$', 'host_job_host_summaries_list'),
|
url(r'^(?P<pk>[0-9]+)/job_host_summaries/$', 'host_job_host_summaries_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/activity_stream/$', 'host_activity_stream_list'),
|
url(r'^(?P<pk>[0-9]+)/activity_stream/$', 'host_activity_stream_list'),
|
||||||
#url(r'^(?P<pk>[0-9]+)/inventory_sources/$', 'host_inventory_sources_list'),
|
url(r'^(?P<pk>[0-9]+)/inventory_sources/$', 'host_inventory_sources_list'),
|
||||||
)
|
)
|
||||||
|
|
||||||
group_urls = patterns('awx.api.views',
|
group_urls = patterns('awx.api.views',
|
||||||
@@ -95,7 +95,7 @@ group_urls = patterns('awx.api.views',
|
|||||||
url(r'^(?P<pk>[0-9]+)/job_host_summaries/$', 'group_job_host_summaries_list'),
|
url(r'^(?P<pk>[0-9]+)/job_host_summaries/$', 'group_job_host_summaries_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/potential_children/$', 'group_potential_children_list'),
|
url(r'^(?P<pk>[0-9]+)/potential_children/$', 'group_potential_children_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/activity_stream/$', 'group_activity_stream_list'),
|
url(r'^(?P<pk>[0-9]+)/activity_stream/$', 'group_activity_stream_list'),
|
||||||
#url(r'^(?P<pk>[0-9]+)/inventory_sources/$', 'group_inventory_sources_list'),
|
url(r'^(?P<pk>[0-9]+)/inventory_sources/$', 'group_inventory_sources_list'),
|
||||||
)
|
)
|
||||||
|
|
||||||
inventory_source_urls = patterns('awx.api.views',
|
inventory_source_urls = patterns('awx.api.views',
|
||||||
@@ -105,8 +105,8 @@ inventory_source_urls = patterns('awx.api.views',
|
|||||||
url(r'^(?P<pk>[0-9]+)/inventory_updates/$', 'inventory_source_updates_list'),
|
url(r'^(?P<pk>[0-9]+)/inventory_updates/$', 'inventory_source_updates_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/activity_stream/$', 'inventory_source_activity_stream_list'),
|
url(r'^(?P<pk>[0-9]+)/activity_stream/$', 'inventory_source_activity_stream_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/schedules/$', 'inventory_source_schedules_list'),
|
url(r'^(?P<pk>[0-9]+)/schedules/$', 'inventory_source_schedules_list'),
|
||||||
#url(r'^(?P<pk>[0-9]+)/groups/$', 'inventory_source_groups_list'),
|
url(r'^(?P<pk>[0-9]+)/groups/$', 'inventory_source_groups_list'),
|
||||||
#url(r'^(?P<pk>[0-9]+)/hosts/$', 'inventory_source_hosts_list'),
|
url(r'^(?P<pk>[0-9]+)/hosts/$', 'inventory_source_hosts_list'),
|
||||||
)
|
)
|
||||||
|
|
||||||
inventory_update_urls = patterns('awx.api.views',
|
inventory_update_urls = patterns('awx.api.views',
|
||||||
|
|||||||
@@ -722,6 +722,14 @@ class HostAllGroupsList(SubListAPIView):
|
|||||||
sublist_qs = parent.all_groups.distinct()
|
sublist_qs = parent.all_groups.distinct()
|
||||||
return qs & sublist_qs
|
return qs & sublist_qs
|
||||||
|
|
||||||
|
class HostInventorySourcesList(SubListAPIView):
|
||||||
|
|
||||||
|
model = InventorySource
|
||||||
|
serializer_class = InventorySourceSerializer
|
||||||
|
parent_model = Host
|
||||||
|
relationship = 'inventory_sources'
|
||||||
|
new_in_148 = True
|
||||||
|
|
||||||
class HostActivityStreamList(SubListAPIView):
|
class HostActivityStreamList(SubListAPIView):
|
||||||
|
|
||||||
model = ActivityStream
|
model = ActivityStream
|
||||||
@@ -815,6 +823,14 @@ class GroupAllHostsList(SubListAPIView):
|
|||||||
sublist_qs = parent.all_hosts.distinct()
|
sublist_qs = parent.all_hosts.distinct()
|
||||||
return qs & sublist_qs
|
return qs & sublist_qs
|
||||||
|
|
||||||
|
class GroupInventorySourcesList(SubListAPIView):
|
||||||
|
|
||||||
|
model = InventorySource
|
||||||
|
serializer_class = InventorySourceSerializer
|
||||||
|
parent_model = Group
|
||||||
|
relationship = 'inventory_sources'
|
||||||
|
new_in_148 = True
|
||||||
|
|
||||||
class GroupActivityStreamList(SubListAPIView):
|
class GroupActivityStreamList(SubListAPIView):
|
||||||
|
|
||||||
model = ActivityStream
|
model = ActivityStream
|
||||||
@@ -980,7 +996,6 @@ class InventorySourceDetail(RetrieveUpdateAPIView):
|
|||||||
serializer_class = InventorySourceSerializer
|
serializer_class = InventorySourceSerializer
|
||||||
new_in_14 = True
|
new_in_14 = True
|
||||||
|
|
||||||
|
|
||||||
class InventorySourceSchedulesList(SubListCreateAPIView):
|
class InventorySourceSchedulesList(SubListCreateAPIView):
|
||||||
|
|
||||||
view_name = "Inventory Source Schedules"
|
view_name = "Inventory Source Schedules"
|
||||||
@@ -1000,6 +1015,22 @@ class InventorySourceActivityStreamList(SubListAPIView):
|
|||||||
relationship = 'activitystream_set'
|
relationship = 'activitystream_set'
|
||||||
new_in_145 = True
|
new_in_145 = True
|
||||||
|
|
||||||
|
class InventorySourceHostsList(SubListAPIView):
|
||||||
|
|
||||||
|
model = Host
|
||||||
|
serializer_class = HostSerializer
|
||||||
|
parent_model = InventorySource
|
||||||
|
relationship = 'hosts'
|
||||||
|
new_in_148 = True
|
||||||
|
|
||||||
|
class InventorySourceGroupsList(SubListAPIView):
|
||||||
|
|
||||||
|
model = Group
|
||||||
|
serializer_class = GroupSerializer
|
||||||
|
parent_model = InventorySource
|
||||||
|
relationship = 'groups'
|
||||||
|
new_in_148 = True
|
||||||
|
|
||||||
class InventorySourceUpdatesList(SubListAPIView):
|
class InventorySourceUpdatesList(SubListAPIView):
|
||||||
|
|
||||||
model = InventoryUpdate
|
model = InventoryUpdate
|
||||||
|
|||||||
@@ -682,6 +682,16 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def clean_source(self):
|
||||||
|
source = self.source
|
||||||
|
if source and self.group:
|
||||||
|
qs = self.group.inventory_sources.filter(source__in=CLOUD_INVENTORY_SOURCES, active=True, group__active=True)
|
||||||
|
existing_sources = qs.exclude(pk=self.pk)
|
||||||
|
if existing_sources.count():
|
||||||
|
s = u', '.join([x.group.name for x in existing_sources])
|
||||||
|
raise ValidationError('Unable to configure this item for cloud sync. It is already managed by %s.' % s)
|
||||||
|
return source
|
||||||
|
|
||||||
|
|
||||||
class InventoryUpdate(UnifiedJob, InventorySourceOptions):
|
class InventoryUpdate(UnifiedJob, InventorySourceOptions):
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -1046,15 +1046,30 @@ class InventoryUpdatesTest(BaseTransactionTest):
|
|||||||
if initial:
|
if initial:
|
||||||
self.assertEqual(inventory.groups.count(), 1)
|
self.assertEqual(inventory.groups.count(), 1)
|
||||||
self.assertEqual(inventory.hosts.count(), 0)
|
self.assertEqual(inventory.hosts.count(), 0)
|
||||||
|
self.assertEqual(inventory_source.groups.count(), 0)
|
||||||
|
self.assertEqual(inventory_source.hosts.count(), 0)
|
||||||
inventory_update = self.check_inventory_update(inventory_source)
|
inventory_update = self.check_inventory_update(inventory_source)
|
||||||
inventory_source = InventorySource.objects.get(pk=inventory_source.pk)
|
inventory_source = InventorySource.objects.get(pk=inventory_source.pk)
|
||||||
self.assertNotEqual(inventory.groups.count(), 1)
|
self.assertNotEqual(inventory.groups.count(), 1)
|
||||||
self.assertNotEqual(inventory.hosts.count(), 0)
|
self.assertNotEqual(inventory.hosts.count(), 0)
|
||||||
|
self.assertNotEqual(inventory_source.groups.count(), 0)
|
||||||
|
self.assertNotEqual(inventory_source.hosts.count(), 0)
|
||||||
|
with self.current_user(self.super_django_user):
|
||||||
|
url = reverse('api:inventory_source_groups_list', args=(inventory_source.pk,))
|
||||||
|
response = self.get(url, expect=200)
|
||||||
|
self.assertNotEqual(response['count'], 0)
|
||||||
|
url = reverse('api:inventory_source_hosts_list', args=(inventory_source.pk,))
|
||||||
|
response = self.get(url, expect=200)
|
||||||
|
self.assertNotEqual(response['count'], 0)
|
||||||
for host in inventory.hosts.all():
|
for host in inventory.hosts.all():
|
||||||
source_pks = host.inventory_sources.values_list('pk', flat=True)
|
source_pks = host.inventory_sources.values_list('pk', flat=True)
|
||||||
self.assertTrue(inventory_source.pk in source_pks)
|
self.assertTrue(inventory_source.pk in source_pks)
|
||||||
self.assertTrue(host.has_inventory_sources)
|
self.assertTrue(host.has_inventory_sources)
|
||||||
self.assertTrue(host.enabled)
|
self.assertTrue(host.enabled)
|
||||||
|
with self.current_user(self.super_django_user):
|
||||||
|
url = reverse('api:host_inventory_sources_list', args=(host.pk,))
|
||||||
|
response = self.get(url, expect=200)
|
||||||
|
self.assertNotEqual(response['count'], 0)
|
||||||
for group in inventory.groups.all():
|
for group in inventory.groups.all():
|
||||||
source_pks = group.inventory_sources.values_list('pk', flat=True)
|
source_pks = group.inventory_sources.values_list('pk', flat=True)
|
||||||
self.assertTrue(inventory_source.pk in source_pks)
|
self.assertTrue(inventory_source.pk in source_pks)
|
||||||
@@ -1062,6 +1077,23 @@ class InventoryUpdatesTest(BaseTransactionTest):
|
|||||||
# Make sure EC2 instance ID groups are excluded.
|
# Make sure EC2 instance ID groups are excluded.
|
||||||
self.assertFalse(re.match(r'^i-[0-9a-f]{8}$', group.name, re.I),
|
self.assertFalse(re.match(r'^i-[0-9a-f]{8}$', group.name, re.I),
|
||||||
group.name)
|
group.name)
|
||||||
|
with self.current_user(self.super_django_user):
|
||||||
|
url = reverse('api:group_inventory_sources_list', args=(group.pk,))
|
||||||
|
response = self.get(url, expect=200)
|
||||||
|
self.assertNotEqual(response['count'], 0)
|
||||||
|
# Try to set a source on a child group that was imported. Should not
|
||||||
|
# be allowed.
|
||||||
|
for group in inventory_source.group.children.all():
|
||||||
|
inv_src_2 = group.inventory_source
|
||||||
|
inv_src_url2 = reverse('api:inventory_source_detail', args=(inv_src_2.pk,))
|
||||||
|
with self.current_user(self.super_django_user):
|
||||||
|
data = self.get(inv_src_url2, expect=200)
|
||||||
|
data.update({
|
||||||
|
'source': inventory_source.source,
|
||||||
|
'credential': inventory_source.credential.pk,
|
||||||
|
})
|
||||||
|
response = self.put(inv_src_url2, data, expect=400)
|
||||||
|
self.assertTrue('source' in response, response)
|
||||||
|
|
||||||
def test_put_inventory_source_detail_with_regions(self):
|
def test_put_inventory_source_detail_with_regions(self):
|
||||||
creds_url = reverse('api:credential_list')
|
creds_url = reverse('api:credential_list')
|
||||||
|
|||||||
Reference in New Issue
Block a user