12d57dc69SVladimir Kotal# 22d57dc69SVladimir Kotal# CDDL HEADER START 32d57dc69SVladimir Kotal# 42d57dc69SVladimir Kotal# The contents of this file are subject to the terms of the 52d57dc69SVladimir Kotal# Common Development and Distribution License (the "License"). 62d57dc69SVladimir Kotal# You may not use this file except in compliance with the License. 72d57dc69SVladimir Kotal# 82d57dc69SVladimir Kotal# See LICENSE.txt included in this distribution for the specific 92d57dc69SVladimir Kotal# language governing permissions and limitations under the License. 102d57dc69SVladimir Kotal# 112d57dc69SVladimir Kotal# When distributing Covered Code, include this CDDL HEADER in each 122d57dc69SVladimir Kotal# file and include the License file at LICENSE.txt. 132d57dc69SVladimir Kotal# If applicable, add the following below this CDDL HEADER, with the 142d57dc69SVladimir Kotal# fields enclosed by brackets "[]" replaced with your own identifying 152d57dc69SVladimir Kotal# information: Portions Copyright [yyyy] [name of copyright owner] 162d57dc69SVladimir Kotal# 172d57dc69SVladimir Kotal# CDDL HEADER END 182d57dc69SVladimir Kotal# 192d57dc69SVladimir Kotal 202d57dc69SVladimir Kotal# 212d97c0a2SVladimir Kotal# Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved. 222d57dc69SVladimir Kotal# 232d57dc69SVladimir Kotal 242d57dc69SVladimir Kotalimport json 252d57dc69SVladimir Kotalimport logging 262d57dc69SVladimir Kotal 272d57dc69SVladimir Kotalimport requests 282d57dc69SVladimir Kotal 292d57dc69SVladimir Kotalfrom .patterns import COMMAND_PROPERTY 302d57dc69SVladimir Kotalfrom .webutil import get_proxies 312d57dc69SVladimir Kotal 322d57dc69SVladimir KotalCONTENT_TYPE = 'Content-Type' 332d57dc69SVladimir KotalAPPLICATION_JSON = 'application/json' # default 342d57dc69SVladimir Kotal 352d57dc69SVladimir Kotal 362d57dc69SVladimir Kotaldef do_api_call(verb, uri, params=None, headers=None, data=None): 372d57dc69SVladimir Kotal """ 382d57dc69SVladimir Kotal Perform an API call. Will raise an exception if the request fails. 392d57dc69SVladimir Kotal :param verb: string holding HTTP verb 402d57dc69SVladimir Kotal :param uri: URI string 412d57dc69SVladimir Kotal :param params: request parameters 422d57dc69SVladimir Kotal :param headers: HTTP headers dictionary 432d57dc69SVladimir Kotal :param data: data or None 442d57dc69SVladimir Kotal :return: the result of the handler call, can be None 452d57dc69SVladimir Kotal """ 462d57dc69SVladimir Kotal logger = logging.getLogger(__name__) 472d57dc69SVladimir Kotal 482d57dc69SVladimir Kotal handler = getattr(requests, verb.lower()) 492d57dc69SVladimir Kotal if handler is None or not callable(handler): 502d57dc69SVladimir Kotal raise Exception('Unknown HTTP verb: {}'.format(verb)) 512d57dc69SVladimir Kotal 522d57dc69SVladimir Kotal logger.debug("{} API call: {} with data '{}' and headers: {}". 532d57dc69SVladimir Kotal format(verb, uri, data, headers)) 542d57dc69SVladimir Kotal r = handler( 552d57dc69SVladimir Kotal uri, 562d57dc69SVladimir Kotal data=data, 572d57dc69SVladimir Kotal params=params, 582d57dc69SVladimir Kotal headers=headers, 592d57dc69SVladimir Kotal proxies=get_proxies(uri) 602d57dc69SVladimir Kotal ) 612d57dc69SVladimir Kotal 622d57dc69SVladimir Kotal if r is None: 632d57dc69SVladimir Kotal raise Exception("API call failed") 642d57dc69SVladimir Kotal 652d57dc69SVladimir Kotal r.raise_for_status() 662d57dc69SVladimir Kotal 672d57dc69SVladimir Kotal return r 682d57dc69SVladimir Kotal 692d57dc69SVladimir Kotal 702d97c0a2SVladimir Kotaldef subst(src, substitutions): 712d97c0a2SVladimir Kotal if substitutions: 722d97c0a2SVladimir Kotal for pattern, value in substitutions.items(): 732d97c0a2SVladimir Kotal if value: 742d97c0a2SVladimir Kotal src = src.replace(pattern, value) 752d97c0a2SVladimir Kotal 762d97c0a2SVladimir Kotal return src 772d97c0a2SVladimir Kotal 782d97c0a2SVladimir Kotal 79*89229afdSVladimir Kotaldef call_rest_api(command, substitutions=None, http_headers=None): 802d57dc69SVladimir Kotal """ 812d57dc69SVladimir Kotal Make RESTful API call. Occurrence of the pattern in the URI 822d57dc69SVladimir Kotal (first part of the command) or data payload will be replaced by the name. 832d57dc69SVladimir Kotal 842d57dc69SVladimir Kotal Default content type is application/json. 852d57dc69SVladimir Kotal 86*89229afdSVladimir Kotal :param command: command (list of URI, HTTP verb, data payload, 87*89229afdSVladimir Kotal HTTP header dictionary) 882d97c0a2SVladimir Kotal :param substitutions: dictionary of pattern:value for command and/or 892d97c0a2SVladimir Kotal data substitution 90*89229afdSVladimir Kotal :param http_headers: optional dictionary of HTTP headers to be appended 912d57dc69SVladimir Kotal :return return value from given requests method 922d57dc69SVladimir Kotal """ 932d57dc69SVladimir Kotal 942d57dc69SVladimir Kotal logger = logging.getLogger(__name__) 952d57dc69SVladimir Kotal 962d57dc69SVladimir Kotal if not isinstance(command, dict) or command.get(COMMAND_PROPERTY) is None: 972d57dc69SVladimir Kotal raise Exception("invalid command") 982d57dc69SVladimir Kotal 992d57dc69SVladimir Kotal command = command[COMMAND_PROPERTY] 1002d57dc69SVladimir Kotal 1012d57dc69SVladimir Kotal uri, verb, data, *_ = command 1022d57dc69SVladimir Kotal try: 1032d57dc69SVladimir Kotal headers = command[3] 1042d57dc69SVladimir Kotal if headers and not isinstance(headers, dict): 1052d57dc69SVladimir Kotal raise Exception("headers must be a dictionary") 1062d57dc69SVladimir Kotal except IndexError: 1072d57dc69SVladimir Kotal headers = {} 1082d57dc69SVladimir Kotal 1092d57dc69SVladimir Kotal if headers is None: 1102d57dc69SVladimir Kotal headers = {} 1112d57dc69SVladimir Kotal 112*89229afdSVladimir Kotal if http_headers: 113*89229afdSVladimir Kotal headers.update(http_headers) 114*89229afdSVladimir Kotal 1152d97c0a2SVladimir Kotal uri = subst(uri, substitutions) 1162d57dc69SVladimir Kotal header_names = [x.lower() for x in headers.keys()] 1172d57dc69SVladimir Kotal 1182d57dc69SVladimir Kotal if data: 1192d57dc69SVladimir Kotal if CONTENT_TYPE.lower() not in header_names: 1202d57dc69SVladimir Kotal logger.debug("Adding header: {} = {}". 1212d57dc69SVladimir Kotal format(CONTENT_TYPE, APPLICATION_JSON)) 1222d57dc69SVladimir Kotal headers[CONTENT_TYPE] = APPLICATION_JSON 1232d57dc69SVladimir Kotal 1242d57dc69SVladimir Kotal for (k, v) in headers.items(): 1252d57dc69SVladimir Kotal if k.lower() == CONTENT_TYPE.lower(): 1262d57dc69SVladimir Kotal if headers[k].lower() == APPLICATION_JSON.lower(): 1272d57dc69SVladimir Kotal logger.debug("Converting {} to JSON".format(data)) 1282d57dc69SVladimir Kotal data = json.dumps(data) 1292d57dc69SVladimir Kotal break 1302d57dc69SVladimir Kotal 1312d97c0a2SVladimir Kotal data = subst(data, substitutions) 1322d57dc69SVladimir Kotal logger.debug("entity data: {}".format(data)) 1332d57dc69SVladimir Kotal 1342d57dc69SVladimir Kotal return do_api_call(verb, uri, headers=headers, data=data) 135