mirror of
https://github.com/ansible/awx.git
synced 2026-01-11 18:09:57 -03:30
tower_group relationships
rollback some module_utils changes add runtime error for 404 type things
This commit is contained in:
parent
ace5a0a2b3
commit
558814ef3b
@ -222,6 +222,8 @@ class TowerModule(AnsibleModule):
|
||||
|
||||
def get_all_endpoint(self, endpoint, *args, **kwargs):
|
||||
response = self.get_endpoint(endpoint, *args, **kwargs)
|
||||
if 'next' not in response['json']:
|
||||
raise RuntimeError('Expected list from API at {0}, got: {1}'.format(endpoint, response))
|
||||
next_page = response['json']['next']
|
||||
|
||||
if response['json']['count'] > 10000:
|
||||
@ -545,47 +547,50 @@ class TowerModule(AnsibleModule):
|
||||
# 2. The response from Tower from patching to the endpoint. It's up to you to process the response and exit from the module.
|
||||
# 3. An ItemNotDefined exception, if the existing_item does not exist
|
||||
# Note: common error codes from the Tower API can cause the module to fail
|
||||
if not existing_item:
|
||||
self.fail_json(msg="The exstiing item is not defined and thus cannot be updated")
|
||||
if existing_item:
|
||||
|
||||
# If we have an item, we can see if it needs an update
|
||||
try:
|
||||
item_url = existing_item['url']
|
||||
item_type = existing_item['type']
|
||||
if item_type == 'user':
|
||||
item_name = existing_item['username']
|
||||
else:
|
||||
item_name = existing_item['name']
|
||||
item_id = existing_item['id']
|
||||
except KeyError as ke:
|
||||
self.fail_json(msg="Unable to process update of item due to missing data {0}".format(ke))
|
||||
# If we have an item, we can see if it needs an update
|
||||
try:
|
||||
item_url = existing_item['url']
|
||||
item_type = existing_item['type']
|
||||
if item_type == 'user':
|
||||
item_name = existing_item['username']
|
||||
else:
|
||||
item_name = existing_item['name']
|
||||
item_id = existing_item['id']
|
||||
except KeyError as ke:
|
||||
self.fail_json(msg="Unable to process update of item due to missing data {0}".format(ke))
|
||||
|
||||
# Check to see if anything within the item requires the item to be updated
|
||||
needs_update = False
|
||||
for field in new_item:
|
||||
existing_field = existing_item.get(field, None)
|
||||
new_field = new_item.get(field, None)
|
||||
# If the two items don't match and we are not comparing '' to None
|
||||
if existing_field != new_field and not (existing_field in (None, '') and new_field == ''):
|
||||
# Something doesn't match so let's update it
|
||||
needs_update = True
|
||||
break
|
||||
# Check to see if anything within the item requires the item to be updated
|
||||
needs_update = False
|
||||
for field in new_item:
|
||||
existing_field = existing_item.get(field, None)
|
||||
new_field = new_item.get(field, None)
|
||||
# If the two items don't match and we are not comparing '' to None
|
||||
if existing_field != new_field and not (existing_field in (None, '') and new_field == ''):
|
||||
# Something doesn't match so let's update it
|
||||
needs_update = True
|
||||
break
|
||||
|
||||
# If we decided the item needs to be updated, update it
|
||||
self.json_output['id'] = item_id
|
||||
if needs_update:
|
||||
response = self.patch_endpoint(item_url, **{'data': new_item})
|
||||
if response['status_code'] == 200:
|
||||
self.json_output['changed'] = True
|
||||
elif 'json' in response and '__all__' in response['json']:
|
||||
self.fail_json(msg=response['json']['__all__'])
|
||||
else:
|
||||
self.fail_json(**{'msg': "Unable to update {0} {1}, see response".format(item_type, item_name), 'response': response})
|
||||
# If we decided the item needs to be updated, update it
|
||||
self.json_output['id'] = item_id
|
||||
if needs_update:
|
||||
response = self.patch_endpoint(item_url, **{'data': new_item})
|
||||
if response['status_code'] == 200:
|
||||
self.json_output['changed'] = True
|
||||
elif 'json' in response and '__all__' in response['json']:
|
||||
self.fail_json(msg=response['json']['__all__'])
|
||||
else:
|
||||
self.fail_json(**{'msg': "Unable to update {0} {1}, see response".format(item_type, item_name), 'response': response})
|
||||
|
||||
else:
|
||||
raise RuntimeError('update_if_needed called incorrectly without existing_item')
|
||||
|
||||
# Process any associations with this item
|
||||
if associations is not None:
|
||||
for association_type in associations:
|
||||
self.modify_associations(response['json']['related'][association_type], associations[association_type])
|
||||
for association_type, id_list in associations.items():
|
||||
endpoint = '{0}{1}/'.format(item_url, association_type)
|
||||
self.modify_associations(endpoint, id_list)
|
||||
|
||||
# If we change something and have an on_change call it
|
||||
if on_update is not None and self.json_output['changed']:
|
||||
|
||||
@ -41,6 +41,18 @@ options:
|
||||
description:
|
||||
- Variables to use for the group.
|
||||
type: dict
|
||||
hosts:
|
||||
description:
|
||||
- List of hosts that should be put in this group.
|
||||
required: False
|
||||
type: list
|
||||
elements: str
|
||||
groups:
|
||||
description:
|
||||
- List of groups that should be nested inside in this group.
|
||||
required: False
|
||||
type: list
|
||||
elements: str
|
||||
state:
|
||||
description:
|
||||
- Desired state of the resource.
|
||||
@ -85,6 +97,8 @@ def main():
|
||||
description=dict(required=False),
|
||||
inventory=dict(required=True),
|
||||
variables=dict(type='dict', required=False),
|
||||
hosts=dict(type='list', elements='str'),
|
||||
groups=dict(type='list', elements='str'),
|
||||
state=dict(choices=['present', 'absent'], default='present'),
|
||||
)
|
||||
|
||||
@ -120,12 +134,31 @@ def main():
|
||||
if variables is not None:
|
||||
group_fields['variables'] = json.dumps(variables)
|
||||
|
||||
association_fields = {}
|
||||
for resource, relationship in (('hosts', 'hosts'), ('groups', 'children')):
|
||||
name_list = module.params.get(resource)
|
||||
if name_list is None:
|
||||
continue
|
||||
id_list = []
|
||||
for sub_name in name_list:
|
||||
sub_obj = module.get_one(resource, **{
|
||||
'data': {'inventory': inventory_id, 'name': sub_name}
|
||||
})
|
||||
if sub_obj is None:
|
||||
module.fail_json(msg='Could not find {0} with name {1}'.format(resource, sub_name))
|
||||
id_list.append(sub_obj['id'])
|
||||
if id_list:
|
||||
association_fields[relationship] = id_list
|
||||
|
||||
if state == 'absent':
|
||||
# If the state was absent we can let the module delete it if needed, the module will handle exiting from this
|
||||
module.delete_if_needed(group)
|
||||
elif state == 'present':
|
||||
# If the state was present we can let the module build or update the existing group, this will return on its own
|
||||
module.create_or_update_if_needed(group, group_fields, endpoint='groups', item_type='group')
|
||||
module.create_or_update_if_needed(
|
||||
group, group_fields, endpoint='groups', item_type='group',
|
||||
associations=association_fields
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@ -3,7 +3,7 @@ __metaclass__ = type
|
||||
|
||||
import pytest
|
||||
|
||||
from awx.main.models import Organization, Inventory, Group
|
||||
from awx.main.models import Organization, Inventory, Group, Host
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@ -32,6 +32,30 @@ def test_create_group(run_module, admin_user):
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_associate_hosts_and_groups(run_module, admin_user, organization):
|
||||
inv = Inventory.objects.create(name='test-inv', organization=organization)
|
||||
group = Group.objects.create(name='Test Group', inventory=inv)
|
||||
|
||||
inv_hosts = [Host.objects.create(inventory=inv, name='foo{0}'.format(i)) for i in range(3)]
|
||||
group.hosts.add(inv_hosts[0], inv_hosts[1])
|
||||
|
||||
child = Group.objects.create(inventory=inv, name='child_group')
|
||||
|
||||
result = run_module('tower_group', dict(
|
||||
name='Test Group',
|
||||
inventory='test-inv',
|
||||
hosts=[inv_hosts[1].name, inv_hosts[2].name],
|
||||
groups=[child.name],
|
||||
state='present'
|
||||
), admin_user)
|
||||
assert not result.get('failed', False), result.get('msg', result)
|
||||
assert result['changed'] is True
|
||||
|
||||
assert set(group.hosts.all()) == set([inv_hosts[1], inv_hosts[2]])
|
||||
assert set(group.children.all()) == set([child])
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_tower_group_idempotent(run_module, admin_user):
|
||||
# https://github.com/ansible/ansible/issues/46803
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user