zPET - IBM Z and z/OS Platform Evaluation and Test - Group home

Automating HMC/SE LPAR control to toggle "Workload Manager Enabled" settings for multiple LPARs by using APIs

  

Are you tired of doing repetitive LPAR control updates from the HMC/SEs?  If your sysplex is like ours, having 15 or more LPARs on 3 different CPCs on a PLEX, you would be as excited as me hearing about how we use APIs in zPET to dynamically update LPAR control values without logging on to the HMC/SEs.  

Today I would like to share with you my experiences using the simple programming language, REXX to invoke a few APIs to update the Workload Manager Enabled Capping values for 15 LPARs on a PLEX all at once.  In zPET, we perform test runs very frequently for different types of tests.  In order to perform our test runs in a more consistent environment and have the result data not to be impacted by processing related weight changes, we like to run tests with the WLM disabled in most cases.  Having a job to dynamically disable the WLM for a test run and later to restore the old value back is extremely convenient and efficient.   The REXX EXEC is made flexible to be executed either in TSO foreground or in background (Batch) via a JCL which may be more preferrable for automation or repetitive tasks.  More good news is that these APIs are free for all z/OS users.  They are provided by the z/OS Base Control Program Internal Interface (BCPii) component which comes with z/OS.  Its address space comes up at IPL as long as you have the proper installation.  For details, please see the “BCPii setup and installation” from https://www.ibm.com/docs/en/zos/2.1.0?topic=services-base-control-program-internal-interface-bcpii or  the "BCPii setup and installation" in the BCPii chapter from the MVS Programming: Callable Services for High-Level Languages publication.

 

If you have not been convinced yet,  check out the following few simple steps!   Even if you are not interested in the WLM change, the infrastructure is there, you can easily implement to make other LPAR control changes.  Here is how the EXEC works:

  1. Obtain the CPC name(s) and their associated LPAR names which are the target systems for the LPAR control change. In our environment, we have 3 CPCs, each CPC has 6 to 8 images across 2 system PLEXes.  We have 3 configuration files which define our current system configuration by using stem variables in REXX.  The followings are some sample stem variable settings for one of the configuration files.      

              CPCNAME = IBM390PS.CPC1             

                IMGCOUNT = n

                IMG.1 = IMGA

                IMG.2 = IMGB      

               IMG.n = IMGn      

  1. Using the target system names, the REXX EXEC will invoke the HWICONN API to connect to each CPC and its associated LPARs. Each connection will generate an output connection token.
  2. For each target LPAR:
    1. Using its connection token, invoke HWIQUERY API to retrieve the current WLM value
    2. If the current WLM value is not the set value, invoke HWISET2 API to set the desired value
    3. Besides checking the return code, for sanity check, invoke HWIQUERY API again to retrieve the newly set WLM value to validate the new value has been set  

 

A few highlights:

  • A test programming mode is used as an input parameter to only retrieve the current WLM value without performing the changes. It’s useful to learn the current configuration before the actual changes.
  • Instead of making the WLM change dynamically to LPARs, you can also make changes in the LPAR activation profiles by specifying the target profile type and the set value will be saved in the profiles and be effective in the next activation.
  • WLM change request is by each target, you can enable the WLM in one LPAR and disable it for others.
  • The complete application including the REXX exec “CHGWLM.rexx” is available in GitHub repository, https://github.com/lowenho/Dynamically-Enable-or-Disable-WLM.



Sample output:    

                   

                    WLM Change Report             18 Mar 2022 08:57:08

        ******************                                

                                                                                           

SYSID     TYPE WLM       Time    Stage                                

----- -------- --- ---------- --------                                

IMGA      LPAR   0   08:57:08     Orig                                

IMGA      LPAR   1   08:57:26  Changed                                

                                                                      

IMGB      LPAR   0   08:57:31     Orig                                

IMGB      LPAR   1   08:57:49  Changed                                 

                                                                      

IMGC      LPAR   0   08:57:53     Orig                                

IMGC      LPAR   1   08:58:09  Changed                                

                                                                       

IMGD   PROFILE   0   08:58:12     Orig                                

IMGD   PROFILE   1   08:58:26  Changed                                

                                                                                                                       

CHGWLM: Change WLM request completed! 18 Mar 2022 09:00:05     

Pseudo code to read in our 3 configuration files. One per CPC:
    • Sample Content in the configuration file for CPC1

                    CPCNAME = IBM390PS.CPC1                                                                                                           

                      IMGCOUNT = 5

                      IMG.1 = IMGA                                                  

                     IMG.2 = IMGB                                                  

                     IMG.3 = IMGC                                                  

                     IMG.4 = IMGD                                                  

                     IMG.5 = IMGE                                                 

/*-----------------------------------------------------------------*/

/* Procedure: ReadinputDD                                          */

/*    - Using EXECIO to read the system configuration file for     */

/*      the desired CPC(s),                                        */

/*    - Using the CPC.0 count to determine how many CPC(s) is      */

/*      provided for processing                                    */

/*                                                                 */

/* Output: where i represents the n-th config. file                */

/*    1. InFile.i.  - stem for the input config. file              */

/*    2. InFile.i.0 - total # of lines for config file             */

/*-----------------------------------------------------------------*/

ReadinputDD:

Do i = 1 to CPC.0

 If Batch = 0 Then

   "ALLOC F(ConfgDD) DA('"ConfigFile.i"') LRECL(137) OLD"

 Select

  When i = 1 Then

   Do

    If Batch = 0 Then

     "EXECIO * DISKR ConfgDD (FINIS STEM InCFile1."

    Else

     "EXECIO * DISKR ConfgDD1 (FINIS STEM InCFile1."

    Do j = 1 to InCFile1.0

     InFile.i.j = InCFile1.j

    End

    InFile.i.0 = InCFile1.0

   End

  When i = 2 Then

   Do

    If Batch = 0 Then

     "EXECIO * DISKR ConfgDD (FINIS STEM InCFile2."

    Else

     "EXECIO * DISKR ConfgDD2 (FINIS STEM InCFile2."

    Do j = 1 to InCFile2.0

     InFile.i.j = InCFile2.j

    End

    InFile.i.0 = InCFile2.0

   End

  When i = 3 Then

   Do

    If Batch = 0 Then

     "EXECIO * DISKR ConfgDD (FINIS STEM InCFile3."

    Else

     "EXECIO * DISKR ConfgDD3 (FINIS STEM InCFile3."

    Do j = 1 to InCFile3.0

     InFile.i.j = InCFile3.j

    End

    InFile.i.0 = InCFile3.0

   End

  Otherwise

   Do

    say

    say '*** Internal Error. Check CPC_Count variable **  '

    say

    Return 8

   End

 End /* End Select */

 If Batch = 0 Then

   "FREE F(ConfgDD)"

End /* End Do */

Return /* End ReadinputDD */ 

Mainline Pseudo code:

/* Copy the BCPii constants to this exec

*/

Call GetBCPiiConstants

 

/* Process one request(target) at a time, using the previously set  

   Parameters which are in stem variables of InTarget_Name and

   InWLMValue.

*/

Do rqi = 1 to rqi.0   /* request index: For each target LPAR */

 

 CPCConnectToken = 0

 TargetConnectToken = 0

 

 /* Call BCPii API to connect to CPCs and Targets

 */

 Call ConnectToTarget InTarget_Name.rqi,InTarget_Type.rqi

 

 TargetToken.rqi = TargetConnectToken

 

 CPCToken.rqi = CPCConnectToken

 

 If Global_RC = 0 Then

   Do

    EachSetRequstRC.rqi = 0 /* each set request has its own RC */

    fp_GetOrig = 1          /* Setting footprint */

 

    /* Call to obtain the original weight values

    */

    Call Get_WLM TargetToken.rqi,InTarget_TypeText.rqi

    OrigTargetName.rqi = RtnTargetName

    OrigWLMValue.rqi   = RtnWLMValue

 

    fp_GetOrig = 0   /* Reset footprint */

 

    /* If not running Test mode, proceed to perform SET request

    */

    If PgmTestMode = 0 Then

     Do

      If InWLMValue.rqi = OrigWLMValue.rqi Then

       Do

        lineIndex = lineIndex + 1

        outLine.lineIndex = ,

          LEFT(OrigTargetName.rqi,5),

          RIGHT(InTarget_TypeText.rqi,8),

          RIGHT(OrigWLMValue.rqi,3),

          RIGHT(Time(),10),

          RIGHT('NoChange',8)

       End

      Else

       Do

        Call SetWLMValue  ,

         CPCToken.rqi,TargetToken.rqi,InWLMValue.rqi,OrigWLMValue.rqi,

                             ,OrigTargetName.rqi

 

        /* If the SET request succeeds, call to obtain the current

           weight values and verify they were changed to the set  

           values

        */

        If Global_RC = 0 & SET_RequestRC = 0 Then

         Do

          fp_Changed = 1

          Call Get_WLM TargetToken.rqi,InTarget_TypeText.rqi

 

 

          /* Check to make sure they were changed to the SET values

          */

          If RtnWLMValue = InWLMValue.rqi Then

            Do

             say 'CHGWLM: WLM value has been set '||,

                 'successfully for '|| OrigTargetName.rqi

            End

          Else

            Do

             /* The returned WLM values does not match to the value

                to be set, report the error

             */

             say 'CHGWLM: ERROR: SET request failed for '|| ,

                  OrigTargetName.rqi

             say 'CHGWLM: WLM value Expected: '||InWLMValue.rqi||,

                  ' Actual: '||RtnWLMValue

             say

             Global_RC = 8

            End

         End

        Else

         Do

          If Global_RC ^= 0 Then

           say 'CHGWLM: Program RC = ' Global_RC

 

          /* Record the failing RC for the current Target and then

             set the failing Target name for error reporting later

          */

          If SET_RequestRC ^= 0 Then

           Do

            EachSetRequstRC.rqi = SET_RequestRC

 

            fi = fi + 1

            FailedTarget.fi = OrigTargetName.rqi

            FailedRC.fi    = EachSetRequstRC.rqi

 

            say 'CHGWLM:  Set request RC = ' EachSetRequstRC.rqi||,

                '('||d2x(EachSetRequstRC.rqi)||'x)'

           End

         End

       End /* End Else if PgmTestMode = 0 */

     End /* End if PgmTestMode = 0 */

   End /* End if Global_RC = 0 */

 

 If CPCConnectRC = 0 Then

  Call Disconnect CPCConnectToken

End /* End do */

 

Exit Global_RC

 

Pseudo code to connect to the target CPC and its associated LPARs:

/*------------------------------------------------------------*/

/*                                                            */

/* Procedure: ConnectToTarget                                 */

/*  Using the input LPAR name to call Get_CPCName to find     */

/*  the associated CPC name which is required for BCPii APIs  */

/*                                                            */

/*  Input: InTarget : LPAR name                               */

/* Output: CPCConnectToken : CPC Token for BCPii API          */

/*         TargetConnectToken : TARGET Token for BCPii API    */

/*                                                            */

/*------------------------------------------------------------*/

ConnectToTarget:

  InTarget = arg(1)

  InTargetType = arg(2)

 

/* Call to obtain the CPC name for the specified LPAR

*/

Call Get_CPCName InTarget

CPCName = RESULT

 

If Global_RC = 0 & CPCName ^= '' Then

 Do

  /* Connect to the CPC to obtain the CPC Connection which is required

     to connect to the LPAR

  */

  Call Connect InConnectToken,HWI_CPC,LEFT(CPCName,17)

 

  If Global_RC = 0 Then

   Do

    CPCConnectRC = 0

    CPCConnectToken = OutConnectToken

 

    /* Call to connect to the input LPAR using the returned CPC token

    */

    If InTargetType = 1 Then

     Call Connect CPCConnectToken,HWI_IMAGE,LEFT(InTarget,17)

    Else

     Call Connect CPCConnectToken,HWI_IMAGE_ACTPROF,LEFT(InTarget,17)

 

    If Global_RC = 0 Then

      TargetConnectToken = OutConnectToken

   End

 End

Else

 Do

  If Global_RC ^= 0 Then

   CPCConnectRC = Global_RC

 

  If CPCName = '' Then

   Do

    Say 'CHGWLM: Program aborted! Unable to obtain the CPC name '||,

        'for '||InTarget

    Say '  Enter a valid LPAR name or check the configuration files!'

   End

 End

 

Return /* ConnectToTargets */

 

Pseudo code to retrieve the current WLM:

/*-----------------------------------------------------------------*/

/* Procedure: Get_WLM                                              */

/*   Call BCPii Query API to retrieve the WLM value for the input  */

/*   target                                                        */

/*-----------------------------------------------------------------*/

Get_WLM:

 

 InConnectToken = arg(1)

 InTargetTypeText = arg(2)

 

 /* Set up the query parm for the HWIQuery API call

 */

 QueryParm.0 = 2

 QueryParm.1.ATTRIBUTEIDENTIFIER = HWI_NAME

 QueryParm.2.ATTRIBUTEIDENTIFIER = HWI_WLM     /* Initial weight */

 

 Call Query InConnectToken

 

 If Global_RC = 0 Then

  Do

   RtnTargetName = QueryParm.1.ATTRIBUTEVALUE

   RtnWLMValue   = QueryParm.2.ATTRIBUTEVALUE

  End

 

If RtnWLMValue = 0 Then

 RtnWLMValue = 0

Else

 RtnWLMValue = STRIP(RtnWLMValue,'L',0)

 

Select

 When fp_GetOrig = 1 Then OutText = 'Orig'

 When fp_Changed = 1 Then OutText = 'Changed'

 Otherwise OutText = ' '

End

 

lineIndex = lineIndex + 1

outLine.lineIndex = ,

    LEFT(RtnTargetName,5),

    RIGHT(InTargetTypeText,8),

    RIGHT(RtnWLMValue,3),

    RIGHT(Time(),10),

    RIGHT(OutText,8)

 

Return /* Get_WLM */     

 

Pseudo code to set the desired WLM value:

/*-----------------------------------------------------------------*/

/* Procedure: SetWLMValue                                          */

/*   Call BCPii SET2 API to change the WLM value                   */

/*   for the input Target represented by the InTargetToken         */

/*---------------------------------------------------------------- */

SetWLMValue:

  InCPCToken    = arg(1)

  InTargetToken = arg(2)

  InWLMValue    = arg(3)

  InOrigWLM     = arg(4)

  InName        = arg(5)

 

  SET_RequestRC = 0

 

    SetParm.0 = 1

    SetParm.1.SET2_CTOKEN = InTargetToken

    SetParm.1.SET2_SETTYPE = HWI_WLM

    SetParm.1.SET2_SETVALUE = InWLMValue

 

    Call Set2 InCPCToken

 

    If Global_RC = HWI_OK Then

      Call WaitTime 10

    Else

     Do

      SET_RequestRC = Global_RC

      say

      say 'CHGWLM: ERROR: SET request failed for '||InName

     End

 

Return /* End SetWLMValue */    

 

Pseudo code for some BCPii API helper procedures:

/*-------------------------------------------------------------*/

/* BCPii HWICONN request                                       */

/*-------------------------------------------------------------*/

Connect:

  InConnectToken = arg(1)

  ConnectType = arg(2)

  ConnectTypeValue = arg(3)

  address bcpii "hwiconn ",

                "ReturnCode ",

                "InConnectToken ",

                "OutConnectToken ",

                "ConnectType ",

                "ConnectTypeValue ",

                "DiagArea."

  REXXHostRc = RC

  If REXXHostRc <> 0 | ReturnCode <> 0 Then

    Do

      say 'REXX RC (decimal) = ' RC

      say 'HWICONN rc (hex) = ' d2x(ReturnCode)

      say 'DiagArea.Diag_Index    = '  DiagArea.Diag_Index

      say 'DiagArea.Diag_Key      = '  DiagArea.Diag_Key

      say 'DiagArea.Diag_Actual   = '  DiagArea.Diag_Actual

      say 'DiagArea.Diag_Expected = '  DiagArea.Diag_Expected

      say 'DiagArea.Diag_CommErr  = '  DiagArea.Diag_CommErr

      say 'DiagArea.Diag_Text     = '  DiagArea.Diag_Text

      say ' '

      say 'Error connecting to ' ConnectTypeValue

      say ' '

    End

If REXXHostRc <> 0 Then

  Global_RC = REXXHostRc

Else

  Global_RC = ReturnCode

 

If Global_RC <> 0 Then

  signal finish

 

Return Global_RC

/*-------------------------------------------------------------*/

/* BCPii HWIDISC request                                       */

/*-------------------------------------------------------------*/

Disconnect:

  InConnectToken = arg(1)

  address bcpii "hwidisc ",

                "ReturnCode ",

                "InConnectToken ",

                "DiagArea."

  REXXHostRc = RC

  If REXXHostRc <> 0 | ReturnCode <> 0 Then

    Do

      say 'REXX RC (decimal) = ' RC

      say 'HWIDISC rc (hex) = ' d2x(ReturnCode)

      say 'DiagArea.Diag_Index    = '  DiagArea.Diag_Index

      say 'DiagArea.Diag_Key      = '  DiagArea.Diag_Key

      say 'DiagArea.Diag_Actual   = '  DiagArea.Diag_Actual

      say 'DiagArea.Diag_Expected = '  DiagArea.Diag_Expected

      say 'DiagArea.Diag_CommErr  = '  DiagArea.Diag_CommErr

      say 'DiagArea.Diag_Text     = '  DiagArea.Diag_Text

    End

  /* No need to worry about disconnect failure as BCPii performs

     implicit disconnect when the task ends

  */

  If REXXHostRc <> 0 Then

    DISC_RC = REXXHostRc

  Else

    DISC_RC = ReturnCode

 

Return DISC_RC

/*-------------------------------------------------------------*/

/* BCPii HWIQUERY request                                      */

/*-------------------------------------------------------------*/

Query:

  InConnectToken = arg(1)

  address bcpii "hwiquery ",

                "ReturnCode ",

                "InConnectToken ",

                "QueryParm. ",

                "DiagArea."

  REXXHostRc = RC

  If REXXHostRc <> 0 | ReturnCode <> 0 Then

    Do

      say 'REXX RC (decimal) = ' RC

      say 'HWIQUERY rc (hex) = ' d2x(ReturnCode)

      say 'DiagArea.Diag_Index    = '  DiagArea.Diag_Index

      say 'DiagArea.Diag_Key      = '  DiagArea.Diag_Key

      say 'DiagArea.Diag_Actual   = '  DiagArea.Diag_Actual

      say 'DiagArea.Diag_Expected = '  DiagArea.Diag_Expected

      say 'DiagArea.Diag_CommErr  = '  DiagArea.Diag_CommErr

      say 'DiagArea.Diag_Text     = '  DiagArea.Diag_Text

    End

  If REXXHostRc <> 0 Then

    Global_RC = REXXHostRc

  Else

    Global_RC = ReturnCode

Return Global_RC

/*-------------------------------------------------------------*/

/* BCPii HWISET2 request                                       */

/*-------------------------------------------------------------*/

Set2:

  InConnectToken = arg(1)

  address bcpii "hwiset2 ",

                "ReturnCode ",

                "InConnectToken ",

                "SetParm. ",

                "DiagArea."

  REXXHostRc = RC

  If REXXHostRc <> 0 | ReturnCode <> 0 Then

    Do

      say 'REXX RC (decimal) = ' RC

      say 'HWISET2 rc (hex) = ' d2x(ReturnCode)

      say 'DiagArea.Diag_Index    = '  DiagArea.Diag_Index

      say 'DiagArea.Diag_Key      = '  DiagArea.Diag_Key

      say 'DiagArea.Diag_Actual   = '  DiagArea.Diag_Actual

      say 'DiagArea.Diag_Expected = '  DiagArea.Diag_Expected

      say 'DiagArea.Diag_CommErr  = '  DiagArea.Diag_CommErr

      say 'DiagArea.Diag_Text     = '  DiagArea.Diag_Text

    End

  If REXXHostRc <> 0 Then

    Global_RC = REXXHostRc

  Else

    Global_RC = ReturnCode

Return Global_RC

/*-------------------------------------------------------------*/

/* Read in BCPii External constants                            */

/*-------------------------------------------------------------*/

GetBCPiiConstants:

"ALLOC F(HWICIREX) DA('SYS1.MACLIB(HWICIREX)') SHR REUS"     

"execio * diskr "HWICIREX" (stem  linelist.  finis   "

"FREE F(HWICIREX)"

do x = 1 to linelist.0

  interpret linelist.x

end

drop linelist.

 

"ALLOC F(HWIC2REX) DA('SYS1.MACLIB(HWIC2REX)') SHR REUS"     

"execio * diskr "HWIC2REX" (stem  linelist.  finis   "

"FREE F(HWIC2REX)"

do x = 1 to linelist.0

  interpret linelist.x

end

drop linelist.

 

Return 0  /* End GetBCPiiConstants */