Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for self-hosted AI server #1

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/__pycache__
4 changes: 4 additions & 0 deletions plugins/module_utils/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
MANIFESTS = 'manifests'
ACTIONS_INSTALL = 'actions/install'
REGISTER_CLUSTER = 'clusters'
REGISTER_INFRASTRUCTURE = 'infra-envs'
2 changes: 2 additions & 0 deletions plugins/module_utils/defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ai_api_endpoint = 'https://api.openshift.com/api/assisted-install/v2'
validate_certificate = True
162 changes: 162 additions & 0 deletions plugins/modules/add_manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#!/usr/bin/python

# Copyright: (c) 2023, Alberto Gonzalez <[email protected]>
# 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
import requests
import os

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.rhpds.assisted_installer.plugins.module_utils import access_token
from ansible_collections.rhpds.assisted_installer.plugins.module_utils import api
from ansible_collections.rhpds.assisted_installer.plugins.module_utils import defaults

import logging
import logging.handlers
my_logger = logging.getLogger('MyLogger')
my_logger.setLevel(logging.DEBUG)
handler = logging.handlers.SysLogHandler(address = '/dev/log')
my_logger.addHandler(handler)

DOCUMENTATION = r'''
---
module: add_manifest
short_description: Downloads files relating to the installed/installing cluster.

version_added: "1.0.0"

description: Downloads files relating to the installed/installing cluster.

options:
ai_api_endpoint:
description: The AI Endpoint
required: false
type: str
validate_certificate:
description: validate the API certificate
required: false
type: bool
cluster_id:
description: ID of the cluster
required: true
type: str
offline_token:
description: Offline token from console.redhat.com
required: true
type: str
file_name:
description: The manifest name.
required: true
type: str
content:
description: The manifest content.
required: true
type: str
folder:
description: The folder for the manifest.
required: true
type: str
author:
- Rabin (@rabin-io)
'''

EXAMPLES = r'''
- name: Add custom manifest
rhpds.assisted_installer.add_manifest:
cluster_id: "{{ newcluster.result.id }}"
offline_token: "{{ offline_token }}"
file_name: "xyz.yaml"
content: "{{ lookup('file', 'manifest.yaml') }}"
folder: manifests
'''

RETURN = r'''
result:
description: Result from the API call
type: dict
returned: always
'''


def run_module():
# define available arguments/parameters a user can pass to the module
module_args = dict(
ai_api_endpoint=dict(type='str', required=False, default=defaults.ai_api_endpoint),
validate_certificate=dict(type='bool', required=False, default=defaults.validate_certificate),
cluster_id=dict(type='str', required=True),
offline_token=dict(type='str', required=True),
file_name=dict(type='str', required=True),
content=dict(type='str', required=True),
folder=dict(type='str', required=False, default='manifests'),
)

session = requests.Session()
adapter = requests.adapters.HTTPAdapter(max_retries=5)
session.mount('https://', adapter)

# seed the result dict in the object
# we primarily care about changed and state
# changed is if this module effectively modified the target
# state will include any data that you want your module to pass back
# for consumption, for example, in a subsequent task
result = dict(
changed=False,
)

# the AnsibleModule object will be our abstraction working with Ansible
# this includes instantiation, a couple of common attr would be the
# args/params passed to the execution, as well as if the module
# supports check mode
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=False
)

headers = {
"Content-Type": "application/json"
}

if module.params['offline_token'] != 'None':
response = access_token._get_access_token(module.params['offline_token'])
if response.status_code != 200:
module.fail_json(msg='Error getting access token ', **response.json())
result['access_token'] = response.json()["access_token"]
headers.update({
"Authorization": "Bearer " + response.json()["access_token"],
})

data = {
"file_name": module.params['file_name'],
"content": module.params['content'],
"folder": module.params['folder']
}

my_logger.debug(str(data))

response = session.post(
f"{module.params['ai_api_endpoint']}/{api.REGISTER_CLUSTER}/{module.params['cluster_id']}/manifests",
headers=headers,
json=data
)

if "code" in response:
module.fail_json(msg='Request failed: ' + str(response.json()))
else:
result['changed'] = True

my_logger.debug(str(response.json()))

result['result'] = response.content

# in the event of a successful module execution, you will want to
# simple AnsibleModule.exit_json(), passing the key/value results
module.exit_json(**result)


def main():
run_module()


if __name__ == '__main__':
main()
39 changes: 31 additions & 8 deletions plugins/modules/create_cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.rhpds.assisted_installer.plugins.module_utils import access_token
from ansible_collections.rhpds.assisted_installer.plugins.module_utils import api
from ansible_collections.rhpds.assisted_installer.plugins.module_utils import defaults

DOCUMENTATION = r'''
---
Expand All @@ -21,6 +23,14 @@
description: Creates a new OpenShift cluster definition using Assisted Installer
options:
ai_api_endpoint:
description: The AI Endpoint
required: false
type: str
validate_certificate:
description: validate the API certificate
required: false
type: bool
name:
description: Name of the cluster
required: true
Expand Down Expand Up @@ -171,6 +181,8 @@
def run_module():
# define available arguments/parameters a user can pass to the module
module_args = dict(
ai_api_endpoint=dict(type='str', required=False, default=defaults.ai_api_endpoint),
validate_certificate=dict(type='bool', required=False, default=defaults.validate_certificate),
name=dict(type='str', required=True),
offline_token=dict(type='str', required=True),
openshift_version=dict(type='str', required=True),
Expand Down Expand Up @@ -215,26 +227,37 @@ def run_module():
module = AnsibleModule(
argument_spec=module_args,
supports_check_mode=True,

)
response = access_token._get_access_token(module.params['offline_token'])
if response.status_code != 200:
module.fail_json(msg='Error getting access token ', **response.json())

headers = {
"Content-Type": "application/json"
}

if module.params['offline_token'] != 'None':
response = access_token._get_access_token(module.params['offline_token'])
if response.status_code != 200:
module.fail_json(msg='Error getting access token ', **response.json())
headers.update({
"Authorization": "Bearer " + response.json()["access_token"],
})

# if the user is working with this module in only check mode we do not
# want to make any changes to the environment, just return the current
# state with no modifications
if module.check_mode:
module.exit_json(**result)
headers = {
"Authorization": "Bearer " + response.json()["access_token"],
"Content-Type": "application/json"
}

params = module.params.copy()
params.pop("offline_token")
params.pop("ai_api_endpoint")
params.pop("validate_certificate")

if "cluster_id" in params:
params.pop("cluster_id")
params["pull_secret"] = json.loads(params["pull_secret"])
response = session.post(
"https://api.openshift.com/api/assisted-install/v2/clusters",
module.params['ai_api_endpoint'] + '/' + api.REGISTER_CLUSTER,
headers=headers,
json=params
)
Expand Down
46 changes: 35 additions & 11 deletions plugins/modules/create_infra_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.rhpds.assisted_installer.plugins.module_utils import access_token
from ansible_collections.rhpds.assisted_installer.plugins.module_utils import api
from ansible_collections.rhpds.assisted_installer.plugins.module_utils import defaults


DOCUMENTATION = r'''
Expand All @@ -22,12 +24,24 @@
description: Creates a new OpenShift Discovery ISO for Assisted Installer
options:
ai_api_endpoint:
description: The AI Endpoint
required: false
type: str
validate_certificate:
description: validate the API certificate
required: false
type: bool
offline_token:
description: Offline token from console.redhat.com
required: true
type: str
additional_ntp_sources:
description: A comma-separated list of NTP sources (name or IP) going to be added to all the hosts.
required: false
type: str
additional_trust_bundle:
description: PEM-encoded X.509 certificate bundle. Hosts discovered by this infra-env will trust the certificates in this bundle. Clusters formed from the hosts discovered by this infra-env will also trust the certificates in this bundle.
description: PEM-encoded X.509 certificate bundle. Hosts discovered by this infra-env will trust the certificates in this bundle. Clusters formed from the hosts discovered by this infra-env will also trust the certificates in this bundle.
required: false
type: str
cluster_id:
Expand Down Expand Up @@ -100,6 +114,8 @@
def run_module():
# define available arguments/parameters a user can pass to the module
module_args = dict(
ai_api_endpoint=dict(type='str', required=False, default=defaults.ai_api_endpoint),
validate_certificate=dict(type='bool', required=False, default=defaults.validate_certificate),
name=dict(type='str', required=True),
offline_token=dict(type='str', required=True),
cluster_id=dict(type='str', required=True),
Expand Down Expand Up @@ -138,27 +154,35 @@ def run_module():
supports_check_mode=True
)

response = access_token._get_access_token(module.params['offline_token'])
if response.status_code != 200:
module.fail_json(msg='Error getting access token ', **response.json())
headers = {
"Content-Type": "application/json"
}

if module.params['offline_token'] != 'None' :
response = access_token._get_access_token(module.params['offline_token'])
if response.status_code != 200:
module.fail_json(msg='Error getting access token ', **response.json())

result['access_token'] = response.json()["access_token"]
headers.update({
"Authorization": "Bearer " + response.json()["access_token"],
})


# if the user is working with this module in only check mode we do not
# want to make any changes to the environment, just return the current
# state with no modifications
if module.check_mode:
module.exit_json(**result)

result['access_token'] = response.json()["access_token"]

headers = {
"Authorization": "Bearer " + response.json()["access_token"],
"Content-Type": "application/json"
}
params = module.params.copy()
params.pop("offline_token")
params.pop("ai_api_endpoint")
params.pop("validate_certificate")

params["pull_secret"] = json.loads(params["pull_secret"])
response = session.post(
"https://api.openshift.com/api/assisted-install/v2/infra-envs",
module.params['ai_api_endpoint'] + '/' + api.REGISTER_INFRASTRUCTURE,
headers=headers,
json=params
)
Expand Down
Loading