bcmapi.py

Business Continuity Manager Web API Reference Guide

Version
9.9.1
Audience
anonymous
Part Number
MK-96HC137-03

########################################################################
#
# 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,""