Andrea Restle-Lay 1f06d1bb9a
[AAP-44277] License module now validates API responses for subscription IDs. (Moved from Tower) (#16096)
* resolve bug and add simple unit tests

* Update awx_collection/plugins/modules/license.py

Co-authored-by: Andrew Potozniak <tyraziel@gmail.com>

---------

Co-authored-by: Andrew Potozniak <tyraziel@gmail.com>
2025-09-22 15:27:47 -04:00

141 lines
4.3 KiB
Python

#!/usr/bin/python
# coding: utf-8 -*-
# (c) 2019, John Westcott IV <john.westcott.iv@redhat.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'}
DOCUMENTATION = '''
---
module: license
author: "John Westcott IV (@john-westcott-iv)"
short_description: Set the license for Automation Platform Controller
description:
- Get or Set Automation Platform Controller license. See
U(https://www.ansible.com/tower) for an overview.
options:
manifest:
description:
- file path to a Red Hat subscription manifest (a .zip file)
required: False
type: str
force:
description:
- By default, the license manifest will only be applied if Tower is currently
unlicensed or trial licensed. When force=true, the license is always applied.
type: bool
default: 'False'
subscription_id:
description:
- Red Hat or Red Hat Satellite subscription_id to attach to
required: False
type: str
state:
description:
- Desired state of the resource.
default: "present"
choices: ["present", "absent"]
type: str
extends_documentation_fragment: awx.awx.auth
'''
RETURN = ''' # '''
EXAMPLES = '''
- name: Set the license using a file
license:
manifest: "/tmp/my_manifest.zip"
- name: Use the subscriptions module to fetch subscriptions from Red Hat or Red Hat Satellite
subscriptions:
username: "my_satellite_username"
password: "my_satellite_password"
- name: Attach to a subscription (requires fetching subscriptions at least once before)
license:
subscription_id: 123456
- name: Remove license
license:
state: absent
'''
import base64
from ..module_utils.controller_api import ControllerAPIModule
def main():
module = ControllerAPIModule(
argument_spec=dict(
manifest=dict(type='str', required=False),
subscription_id=dict(type='str', required=False),
force=dict(type='bool', default=False),
state=dict(choices=['present', 'absent'], default='present'),
),
required_if=[
['state', 'present', ['manifest', 'subscription_id'], True],
],
mutually_exclusive=[("manifest", "subscription_id")],
)
json_output = {'changed': False}
# If the state was absent we can delete the endpoint and exit.
state = module.params.get('state')
if state == 'absent':
module.delete_endpoint('config')
module.exit_json(**json_output)
if module.params.get('manifest', None):
try:
with open(module.params.get('manifest'), 'rb') as fid:
manifest = base64.b64encode(fid.read())
except OSError as e:
module.fail_json(msg=str(e))
# Check if Tower is already licensed
config = module.get_endpoint('config')['json']
already_licensed = (
'license_info' in config
and 'instance_count' in config['license_info']
and config['license_info']['instance_count'] > 0
and 'trial' in config['license_info']
and not config['license_info']['trial']
)
# Determine if we will install the license
perform_install = bool((not already_licensed) or module.params.get('force'))
# Handle check mode
if module.check_mode:
json_output['changed'] = perform_install
module.exit_json(**json_output)
# Do the actual install, if we need to
if perform_install:
if module.params.get('manifest', None):
response = module.post_endpoint('config', data={'manifest': manifest.decode()})
else:
response = module.post_endpoint('config/attach', data={'subscription_id': module.params.get('subscription_id')})
# Check API response for errors (AAP-44277 fix)
if response and response.get('status_code') and response.get('status_code') != 200:
error_msg = response.get('json', {}).get('error', 'License operation failed')
module.fail_json(msg=error_msg)
json_output['changed'] = True
module.exit_json(**json_output)
if __name__ == '__main__':
main()