You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
146 lines
6.2 KiB
146 lines
6.2 KiB
# Copyright (C) 2005-2024 Splunk Inc. All Rights Reserved.
|
|
import sys
|
|
import time
|
|
|
|
from splunk.clilib.bundle_paths import make_splunkhome_path
|
|
sys.path.append(make_splunkhome_path(['etc', 'apps', 'SA-ITOA', 'lib']))
|
|
import itsi_path
|
|
from itsi_py3 import _
|
|
import itsi_py3
|
|
|
|
import requests
|
|
import base64
|
|
import json
|
|
import http.client
|
|
from itsi.data_integrations.data_integrations_constants import *
|
|
from ITOA.itoa_exceptions import ItoaError
|
|
from itsi.data_integrations.utils.secret_store_manager import StoragePasswordManager
|
|
from itsi.data_integrations.utils.error_manager import is_retryable, get_sleep_time
|
|
|
|
env = 'production'
|
|
|
|
url = {
|
|
'playground':
|
|
{
|
|
'auth': 'https://auth.playground.scp.splunk.com',
|
|
'app': 'https://app.playground.scp.splunk.com',
|
|
},
|
|
'staging':
|
|
{
|
|
'auth': 'https://auth.staging.scp.splunk.com',
|
|
'app': 'https://app.staging.scp.splunk.com',
|
|
},
|
|
'production':
|
|
{
|
|
'auth': 'https://auth.scp.splunk.com',
|
|
'app': 'https://app.scp.splunk.com',
|
|
}
|
|
}
|
|
|
|
class ScpInterface:
|
|
def __init__(self, logger):
|
|
self.logger = logger
|
|
self.password_manager = StoragePasswordManager(self.logger)
|
|
|
|
def get_headers_for_app_saimc_service(self, kvstore_collection, splunk_rest_client, need_auth):
|
|
"""
|
|
Return headers for the REST call to be made to saimulticloudprovisioning service
|
|
"""
|
|
if not need_auth:
|
|
self.logger.debug('Returning headers with no bearer token')
|
|
return {'Content-Type': 'application/json'}
|
|
|
|
client_id = ""
|
|
|
|
try:
|
|
# KV store wait is already done, so we don't need to call it again here
|
|
cloud_config = kvstore_collection.load()
|
|
client_id = cloud_config[0].get('clientId')
|
|
except Exception as e:
|
|
self.logger.error('Error while loading kvstore collection', props={ERROR_DETAIL_LOGGING_KEY: e,
|
|
COLLECTION_LOGGING_KEY: self.collection})
|
|
raise e
|
|
|
|
if not client_id:
|
|
self.logger.error('Could not find the client id', props={COLLECTION_LOGGING_KEY: self.collection})
|
|
raise ItoaError(_('Could not find the client id in the collection'), self.logger)
|
|
|
|
secret = self.password_manager.get_secret(splunk_rest_client, client_id, key_type='client_id')
|
|
|
|
b64val = base64.b64encode(itsi_py3.to_bytes('%s:%s' % (client_id, secret)))
|
|
auth_header = {'Authorization': 'Basic %s' % itsi_py3.decode(b64val)}
|
|
|
|
|
|
bearer_token_response = self.handle_scp_calls(cluster='auth',
|
|
method='POST',
|
|
endpoint=CLOUD_SETUP_IAC_VALIDATION,
|
|
data={'grant_type': 'client_credentials'},
|
|
header=auth_header)
|
|
|
|
status_code = bearer_token_response.status_code
|
|
json_data = {}
|
|
if bearer_token_response.text:
|
|
json_data = json.loads(bearer_token_response.text)
|
|
|
|
if status_code != http.client.OK:
|
|
self.logger.error('Auth credential flow failed ', props={STATUS_CODE_LOGGING_KEY: status_code,
|
|
CLIENT_ID_LOGGING_KEY: client_id})
|
|
raise ItoaError(('%s returned from auth with credential flow' % status_code), self.logger)
|
|
|
|
return {'Content-Type': 'application/json', 'Authorization': 'Bearer %s' % str(json_data['access_token'])}
|
|
|
|
def handle_scp_calls(self, cluster, method, endpoint, data=None, header=None):
|
|
"""
|
|
Construct the REST request to SCP, send it and return the response
|
|
"""
|
|
_endpoint = str(endpoint)
|
|
if _endpoint[0] != '/':
|
|
raise ItoaError(_('Endpoint error'), self.logger)
|
|
|
|
scp_url = url[env][cluster] + _endpoint
|
|
payloads = data
|
|
|
|
# If the header indicates that the content type is JSON, set payloads to the JSON string
|
|
if header:
|
|
content_type = header.get('Content-Type', '')
|
|
if content_type == 'application/json':
|
|
payloads = json.dumps(data)
|
|
|
|
response = {}
|
|
|
|
tries = 0
|
|
maxRetrys = 3
|
|
while True:
|
|
self.logger.info('Invoking SCP endpoint', props={SCP_METHOD_LOGGING_KEY: method,
|
|
ENDPOINT_LOGGING_KEY: _endpoint,
|
|
RETRY_COUNT_LOGGING_KEY: tries,
|
|
SCP_CLUSTER_LOGGING_KEY: cluster})
|
|
|
|
if method == 'POST':
|
|
response = requests.post(scp_url, headers=header, data=payloads)
|
|
elif method == 'PUT':
|
|
response = requests.put(scp_url, headers=header, data=payloads)
|
|
elif method == 'GET':
|
|
response = requests.get(scp_url, headers=header, data=payloads)
|
|
elif method == 'DELETE':
|
|
response = requests.delete(scp_url, headers=header, data=payloads)
|
|
|
|
if response == {}:
|
|
self.logger.error('Empty response from SCP',
|
|
props={SCP_METHOD_LOGGING_KEY: method, ENDPOINT_LOGGING_KEY: _endpoint,
|
|
RETRY_COUNT_LOGGING_KEY: tries, SCP_CLUSTER_LOGGING_KEY: cluster })
|
|
raise ItoaError(_('Failed to get a response from SCP'), self.logger)
|
|
|
|
self.logger.info('SCP response received',
|
|
props={SCP_METHOD_LOGGING_KEY: method, ENDPOINT_LOGGING_KEY: _endpoint,
|
|
RETRY_COUNT_LOGGING_KEY: tries, SCP_CLUSTER_LOGGING_KEY: cluster,
|
|
STATUS_CODE_LOGGING_KEY: response.status_code})
|
|
|
|
tries = tries + 1
|
|
if (300 > response.status_code > 199) or (is_retryable(response.status_code, self.logger) == False) or (tries == maxRetrys):
|
|
break
|
|
|
|
time.sleep(get_sleep_time(tries))
|
|
|
|
return response
|