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:
Chris Church 2014-03-31 19:26:03 -04:00
parent d7f31f9777
commit 008ec8f86e
5 changed files with 82 additions and 9 deletions

View File

@ -722,7 +722,7 @@ class HostSerializer(BaseSerializerWithVariables):
job_events = reverse('api:host_job_events_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,)),
#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:
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_host_summaries = reverse('api:group_job_host_summaries_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:
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,)),
schedules = reverse('api:inventory_source_schedules_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,)),
#groups = reverse('api:inventory_source_groups_list', args=(obj.pk,)),
hosts = reverse('api:inventory_source_hosts_list', args=(obj.pk,)),
groups = reverse('api:inventory_source_groups_list', args=(obj.pk,)),
))
if obj.inventory and obj.inventory.active:
res['inventory'] = reverse('api:inventory_detail', args=(obj.inventory.pk,))

View File

@ -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_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]+)/inventory_sources/$', 'host_inventory_sources_list'),
url(r'^(?P<pk>[0-9]+)/inventory_sources/$', 'host_inventory_sources_list'),
)
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]+)/potential_children/$', 'group_potential_children_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',
@ -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]+)/activity_stream/$', 'inventory_source_activity_stream_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]+)/hosts/$', 'inventory_source_hosts_list'),
url(r'^(?P<pk>[0-9]+)/groups/$', 'inventory_source_groups_list'),
url(r'^(?P<pk>[0-9]+)/hosts/$', 'inventory_source_hosts_list'),
)
inventory_update_urls = patterns('awx.api.views',

View File

@ -722,6 +722,14 @@ class HostAllGroupsList(SubListAPIView):
sublist_qs = parent.all_groups.distinct()
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):
model = ActivityStream
@ -815,6 +823,14 @@ class GroupAllHostsList(SubListAPIView):
sublist_qs = parent.all_hosts.distinct()
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):
model = ActivityStream
@ -980,7 +996,6 @@ class InventorySourceDetail(RetrieveUpdateAPIView):
serializer_class = InventorySourceSerializer
new_in_14 = True
class InventorySourceSchedulesList(SubListCreateAPIView):
view_name = "Inventory Source Schedules"
@ -1000,6 +1015,22 @@ class InventorySourceActivityStreamList(SubListAPIView):
relationship = 'activitystream_set'
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):
model = InventoryUpdate

View File

@ -682,6 +682,16 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions):
return True
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):
'''

View File

@ -1046,15 +1046,30 @@ class InventoryUpdatesTest(BaseTransactionTest):
if initial:
self.assertEqual(inventory.groups.count(), 1)
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_source = InventorySource.objects.get(pk=inventory_source.pk)
self.assertNotEqual(inventory.groups.count(), 1)
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():
source_pks = host.inventory_sources.values_list('pk', flat=True)
self.assertTrue(inventory_source.pk in source_pks)
self.assertTrue(host.has_inventory_sources)
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():
source_pks = group.inventory_sources.values_list('pk', flat=True)
self.assertTrue(inventory_source.pk in source_pks)
@ -1062,6 +1077,23 @@ class InventoryUpdatesTest(BaseTransactionTest):
# Make sure EC2 instance ID groups are excluded.
self.assertFalse(re.match(r'^i-[0-9a-f]{8}$', group.name, re.I),
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):
creds_url = reverse('api:credential_list')