######################################################################## # # All Rights Reserved. # Copyright (C) 2024, Hitachi Vantara, Ltd. # ######################################################################## # # bcmapi - This sample script is a group of functions called from # another BCM Web API sample script. # # Note: # 1) This script needs to be placed in the same directory as the calling # source script. # 2) The calling source script must include "import bcmapi". # ######################################################################## import ssl import urllib.request import json import pprint import time import base64 import configparser import os # global variables cookie = "" hostaddr = "" servletKey = None queueID = None verbose = False bcmStart = False clistMBN = "YKAPIPRC" # Get value from config file. def get_config(conf,section,varName,default=None): try: varValue = conf.get(section,varName) except configparser.NoOptionError as err: if default is not None: return default print(err) exit(-1) except Exception as err: print(err) exit(-1) return varValue # Read config file. def read_config(confpath="bcmapi.ini"): global verbose conf = configparser.ConfigParser() if not os.path.exists(confpath): print('Config file "' + confpath + '" not found.') exit(-1) try: conf.read(confpath) except Exception as err: print(err) exit(-1) verbose = get_config(conf, 'bcmapi', 'verbose' , 'FALSE') if verbose.upper() == "FALSE": verbose = False elif verbose.upper() == "TRUE": verbose = True else: print("verbose value is invalid.") exit(-1) return conf # Start TSO/E address space for BCM Web API server. def start_tso(confpath="bcmapi.ini"): global hostaddr, servletKey, queueID, clistMBN # read config conf = read_config(confpath) startTsoPrm = \ 'proc=' + get_config(conf, 'startTsoPrm', 'proc' ,'IKJACCNT') \ + '&rsize=' + get_config(conf, 'startTsoPrm', 'rsize','50000') \ + '&acct=' + get_config(conf, 'startTsoPrm', 'acct' ,'DEFAULT') \ + '&chset=697&cpage=1047&rows=204&cols=160' certFile = get_config(conf, 'zosmf', 'cert', '') keyFile = get_config(conf, 'zosmf', 'key', '') if certFile == "": userid = get_config(conf, 'zosmf', 'userid') password = input("Please input password of z/OS userid("+userid+"):") hostaddr = get_config(conf, 'zosmf', 'hostaddr') clistMBN = get_config(conf, 'bcmapi', 'clist', 'YKAPIPRC') # Set SSL context sslcontext = ssl.create_default_context() sslcontext.check_hostname = False sslcontext.verify_mode = ssl.CERT_NONE if certFile != "": try: if keyFile == "": sslcontext.load_cert_chain(certFile) else: sslcontext.load_cert_chain(certFile, keyFile) except Exception as err: print("Cannot load client cert file or private key file.") exit(-1) opener = urllib.request.build_opener(\ urllib.request.HTTPSHandler(context=sslcontext)) urllib.request.install_opener(opener) # Set basic authentication header for z/OSMF authentication. if certFile == "": userpwd = userid + ':' + password basicAuthToken = base64.b64encode(userpwd.encode()).decode() headers = { 'Authorization': 'Basic ' + basicAuthToken, 'X-CSRF-ZOSMF-HEADER': 'yes', 'Content-Type': 'application/json' } else: headers = { 'X-CSRF-ZOSMF-HEADER': 'yes', 'Content-Type': 'application/json' } # Send REST API of TSO/E address space creation request to z/OSMF. url = 'https://' + hostaddr + '/zosmf/tsoApp/tso' qsl = urllib.parse.parse_qsl(startTsoPrm) url = url + '?' + urllib.parse.urlencode(qsl) req = urllib.request.Request(url, None, headers, method='POST') if verbose: print('POST', url) try: with urllib.request.urlopen(req, timeout=10) as resp: respBody = json.load(resp) global cookie cookie = resp.getheader('Set-Cookie') except urllib.error.HTTPError as err: print("Http error. Status code=",err.code) print() return "" except urllib.error.URLError as err: print("Cannot connect to z/OSMF. reason=",err.reason) print() return "" except Exception as err: print("Cannot connect to z/OSMF. ",err.args) print() return "" if verbose: print(respBody) if 'servletKey' in respBody: servletKey = respBody["servletKey"] else: servletKey = None if 'queueID' in respBody: queueID = respBody["queueID"] else: queueID = None logonInProgress = False logonReady = False logonFailed = False method = 'POST' while not logonFailed: if 'tsoData' in respBody: for tsoMsg in respBody["tsoData"]: if 'TSO MESSAGE' in tsoMsg: msgStr = tsoMsg["TSO MESSAGE"]["DATA"] if method!='GET': print(msgStr) if "LOGON IN PROGRESS AT" in msgStr: logonInProgress = True elif "LOGGED OFF TSO AT" in msgStr: logonInProgress = False elif msgStr.strip() == "READY": logonReady = True if 'TSO PROMPT' in tsoMsg: if logonReady == True: return respBody if logonInProgress: uri = '/zosmf/tsoApp/tso/'+ servletKey method = 'GET' respBody = zosmfreq(method, uri, '') else: logonFailed = True else: if 'msgData' in respBody: for msgData in respBody["msgData"]: if 'messageText' in msgData: print(msgData["messageText"]) if 'timeout' in respBody: timeout = respBody["timeout"] if timeout==True: print("logon timeout") logonFailed = True # The user will be logged out # because the TSO/E logon validation test did not finish. if servletKey: uri = '/zosmf/tsoApp/tso/' + servletKey zosmfreq('DELETE', uri, '') servletKey = None return "" # Start BCM Web API server. def start_bcm(): global bcmStart if not servletKey or not queueID: return "" uri = '/zosmf/tsoApp/app/' + servletKey + '/YKAPI' startcmd = clistMBN + " &1 &2 " + queueID; reqBody = '{"startcmd":"' + startcmd + '"}' respBody = zosmfreq('POST', uri, reqBody) if 'tsoData' in respBody: for tsoMsg in respBody["tsoData"]: if 'TSO MESSAGE' in tsoMsg: msgStr = tsoMsg["TSO MESSAGE"]["DATA"] if msgStr.split(' ')[0] == "YK7390I": bcmStart = True return respBody return "" # End BCM Web API server. def end_bcm(): global bcmStart if not servletKey: return "" bcmStart = False uri = '/zosmf/tsoApp/app/' + servletKey + '/YKAPI' reqBody = '{"action":"exit"}' return zosmfreq('PUT', uri, reqBody) # End TSO/E address space. def end_tso(): global servletKey,bcmStart if not servletKey: return "" bcmStart = False uri = '/zosmf/tsoApp/tso/' + servletKey servletKey = None return zosmfreq('DELETE', uri, '') # The z/OSMF REST API of the TSO/E address space service # in which a BCM Web API request is embedded will be sent. def zosmfreq(method, uri, reqBodyStr): global cookie # Generate header and requestbody of z/OSMF REST API. url = 'https://' + hostaddr + uri if not reqBodyStr: reqBodyDict = None else: reqBodyDict = json.loads(reqBodyStr) headers = { 'X-CSRF-ZOSMF-HEADER': 'yes', 'Content-Type': 'application/json' } req = urllib.request.Request(\ url, json.dumps(reqBodyDict).encode(), headers, method=method) req.add_header('Cookie', cookie) print(method, uri, reqBodyStr) # Send the z/OSMF REST API request and parse the response. try: with urllib.request.urlopen(req) as resp: respBody = json.load(resp) except urllib.error.HTTPError as err: print("Http error. Status code=", err.code) print() return "" except urllib.error.URLError as err: print("Cannot connect to z/OSMF. reason=",err.reason) print() return "" except Exception as err: print("Cannot connect to z/OSMF. ",err.args) print() return "" if verbose: print(respBody) if 'tsoData' in respBody: for tsoMsg in respBody["tsoData"]: if 'TSO MESSAGE' in tsoMsg: print(tsoMsg["TSO MESSAGE"]["DATA"]) return respBody elif 'msgData' in respBody: for msgData in respBody["msgData"]: if 'messageText' in msgData: print(msgData["messageText"]) return "" return respBody # Send BCM Web API request and receive the response. def bcmreq(cliname, operands="", cliparms=None, *, \ offset="", limit="", fields="", interval=10): # check wrong character is include or not if (not isinstance(cliname, str)) or \ (not cliname.isalnum()) or (not cliname.isascii()): print("Cliname key's value is invalid.") return 99,"" if (not isinstance(operands, str)) or (not operands.isprintable()): print("Operands key's value is invalid.") return 99,"" operands = operands.replace('\\','\\\\') operands = operands.replace('"','\\"').replace('/','\\/') if cliparms is not None: if not isinstance(cliparms, list): print("CLIPARMS key's value is invalid.") return 99,"" for i,record in enumerate(cliparms): if (not isinstance(record, str)) or (not record.isprintable()): print("CLIPARMS key's [",i,"] value is invalid.") return 99,"" cliparms[i] = cliparms[i].replace('\\','\\\\') cliparms[i] = cliparms[i].replace('"','\\"').replace('/','\\/') if len(cliparms[i])>80: print("CLIPARMS key's [",i,"] value exceeds 80 characters") return 99,"" # Generate requestbody reqBodyStr = '{ "cliname": "' + cliname + '"' if type(cliparms) == list: records = "" for record in cliparms: if records != "": records = records + "," records = records + '"' + record + '"' reqBodyStr = reqBodyStr + ', "CLIPARMS": [' + records + ']' if offset != "": reqBodyStr = reqBodyStr + ',"offset": ' + str(offset) if limit != "": reqBodyStr = reqBodyStr + ',"limit": ' + str(limit) if fields != "": reqBodyStr = reqBodyStr + ',"fields": "' + fields + '"' reqBodyStr = reqBodyStr + ', "operands": "' + operands + '"' + '}' # Send BCM Web API request to z/OSMF. if not servletKey: return 99,"" uri = '/zosmf/tsoApp/app/' + servletKey + '/YKAPI' respBody = zosmfreq('PUT', uri, reqBodyStr) if not respBody: return 99,"" # If a timeout occurs, # a BCM Web API response-receive request will be sent. timeout = respBody["timeout"] while timeout: time.sleep(interval) uri = '/zosmf/tsoApp/app/'+ servletKey + '/YKAPI' respBody = zosmfreq('GET', uri, '') if not respBody: return 99,"" timeout = respBody["timeout"] # Parse BCM Web API response for the appData key # if a timeout did not occurred. if 'appData' in respBody: appData = respBody["appData"] bcmapiRC = appData["rc"] print(cliname,'ended. rc =',bcmapiRC) for bcmMsg in appData["bcmMsg"]: if 'msgValue' in bcmMsg.keys(): print(bcmMsg["msgText"], 'msgValue=', bcmMsg["msgValue"]) else: print(bcmMsg["msgText"]) return bcmapiRC, appData else: return 99,""