import base64
import http.client
import json
from jwt import decode
from ibmcloud_python_sdk.config import params
from ibmcloud_python_sdk.utils import cache
def _account_id(headers):
"""Retrieve BSS ID and encode it to base64
:param headers: Headers to parse
:type headers: dict
:return: BSS ID encoded to base64
:rtype: str
"""
auth = headers.get("Authorization")
if auth:
# Split the Bearer token and decode the JWT
jwt = decode(auth.split(" ")[1], verify=False)
# Encode BSS ID to base64
encoded = base64.b64encode(jwt["account"]["bss"].encode("utf-8"))
# Returns base64 string
return encoded.decode()
[docs]def query_wrapper(conn_type, method, path, headers=None, payload=None):
"""Execute HTTP query and return JSON response
:param conn_type: Define which URL should be used for the connection
such as "iaas", "auth", "cis", or "rg" (resource group)
:type conn_type: str
:param method: HTTP method that should be used such as
GET, POST, PUT, DELETE, etc...
:type method: str
:param path: Path used by within the query
:type path: str
:param headers: Headers to send with the query is required such
authentication token, content type, etc...
:type headers: dict, optional
:param payload: JSON payload send during the query
:type payload: dict, optional
:return: JSON response
:rtype: dict
"""
cfg = params()
timeout = cfg["http_timeout"]
if conn_type == "iaas":
conn = http.client.HTTPSConnection(cfg["is_url"], timeout=timeout)
elif conn_type == "rg":
conn = http.client.HTTPSConnection(cfg["rg_url"], timeout=timeout)
elif conn_type == "auth":
conn = http.client.HTTPSConnection(cfg["auth_url"], timeout=timeout)
elif conn_type == "dns":
conn = http.client.HTTPSConnection(cfg["dns_url"], timeout=timeout)
elif conn_type == "em":
conn = http.client.HTTPSConnection(cfg["em_url"], timeout=timeout)
elif conn_type == "sl":
if headers and cfg["cis_username"] and cfg["cis_apikey"]:
header = base64.encodebytes(
('%s:%s' % (cfg["cis_username"], cfg["cis_apikey"]))
.encode('utf8')).decode('utf8').replace('\n', '')
headers["Authorization"] = "Basic {}".format(header)
conn = http.client.HTTPSConnection(cfg["sl_url"], timeout=timeout)
elif conn_type == "power":
conn = http.client.HTTPSConnection(cfg["pi_url"], timeout=timeout)
if cache.client():
if method == "GET" and conn_type != "auth":
obj = "{}{}".format(_account_id(headers), path)
item = cache.get_item(obj)
if item is not None:
return {"data": json.loads(item.decode("utf-8"))}
else:
pass
conn.request(method, path, payload, headers)
# Get and read response data
res = conn.getresponse()
data = res.read()
if not data:
# Return empty data and HTTP response this is mostly
# due to DELETE request which doesn't return any data
return {"data": None, "response": res}
else:
if cache.client():
obj = "{}{}".format(_account_id(headers), path)
# Store item into caching system
cache.set_item(obj, data)
# Return data and HTTP response
return {"data": json.loads(data), "response": res}
[docs]def check_args(arguments, **kwargs):
"""Check that required arguments are passed to the function
:param arguments: List of required arguments
:type arguments: list
"""
# Argument required by the function
required = set(arguments)
# Argument passed to the function
passed = set(kwargs.keys())
# Check if required arguments are passed
if not required.issubset(passed):
raise KeyError(
"Required param(s) is/are missing. Required: {}".format(required)
)
[docs]def resource_not_found(payload=None):
"""Return custom JSON if a resource is not found
:param payload: Customize the JSON to return if needed
:type payload: dict, optional
:return: A JSON dict with a message
:rtype: dict
"""
if payload is not None:
return payload
else:
return {"errors": [{"code": "not_found"}]}
[docs]def resource_deleted(payload=None):
"""Return custom JSON if a resource is deleted
:param payload: Customize the JSON to return if needed
:type payload: dict, optional
:return: A JSON dict with a message
:rtype: dict
"""
if payload is not None:
return payload
else:
return {"status": "deleted"}
[docs]def resource_found(payload=None):
"""Return custom JSON if a resource is found but doesn't have output
:param payload: Customize the JSON to return if needed
:type payload: dict, optional
"""
if payload is not None:
return payload
else:
return {"status": "found"}
[docs]def resource_created(payload=None):
"""Return custom JSON if a resource is created but doesn't have output
:param payload: Customize the JSON to return if needed
:type payload: dict, optional
:return: A JSON dict with a message
:rtype: dict
"""
if payload is not None:
return payload
else:
return {"status": "created"}
[docs]def resource_error(code, message):
"""Return custom JSON if a resource raised an exception. This will be
mostly used during try: / except: on SoftLayer resources
:param code: Code to return
:type code: int
:param message: Message to return
:type messaage: str
:return: A JSON dict with an error message
:rtype: dict
"""
if code == 404:
code = "not_found"
return {"errors": {"code": code, "message": message}}