Overview

Skill Level: Intermediate

Some systems programming experience is an advantage, but no such knowledge is assumed

This recipe sets out the process for creating and using duplicate resident volumes (RESVOLs) for the November 2019 ADCD build of z/OS. Applying PTFs to a running z/OS instance can cause stability issues. Duplicate RESVOLs avoid this problem.

Ingredients

zPDT or IBM Z Development and Test running an ADCD build of z/OS.  This recipe refers to the November 2019 z/OS build, but the same steps apply to any ADCD z/OS release, with minor modifications.

Log on to z/OS using IBMUSER or ADCDMST for these steps, so you have the required RACF permissions for each step.

Step-by-step

  1. Allocate duplicate volumes

    For each of the ADCD RESVOLs (those with volume serials of the form xxRESn) create 2 new volumes, using the zPDT alcckd command in Linux:

    alcckd -d 3390-9 x4res1
    alcckd -d 3390-9 y4res1
    alcckd -d 3390-9 x4res2
    alcckd -d 3390-9 y4res2

     Update your devmap to add these new volumes, assigning each to an unused device address, for example:

    device 0ad1 3390 3390 /home/ibmsys1/z1090/x4res1
    device 0ad2 3390 3390 /home/ibmsys1/z1090/x4res2
    device 0ae1 3390 3390 /home/ibmsys1/z1090/y4res1
    device 0ae2 3390 3390 /home/ibmsys1/z1090/y4res2

     Re-IPL z/OS to pick up these new volumes.

  2. Format duplicate volumes

    Create and submit JCL to format each of these new volumes.  For example:

    //ICKDSF JOB NOTIFY=&SYSUID 
    //DO EXEC PGM=ICKDSF,REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSIN DD *
    INIT UNIT(AD1) NOVERIFY VOLID(X4RES1) VTOC(0,1,29) INDEX(2,0,15)
    INIT UNIT(AD2) NOVERIFY VOLID(X4RES2) VTOC(0,1,29) INDEX(2,0,15)
    INIT UNIT(AE1) NOVERIFY VOLID(Y4RES1) VTOC(0,1,29) INDEX(2,0,15)
    INIT UNIT(AE2) NOVERIFY VOLID(Y4RES2) VTOC(0,1,29) INDEX(2,0,15)
    /*

    ¬†Reply to each “ICK003D REPLY U TO ALTER VOLUME 0Axx CONTENTS, ELSE T” message on the operator console with:

    nn,U

    After each volume is successfully formatted, vary them online from the operator console:

    V AD1,ONLINE
    V AD2,ONLINE
    V AE1,ONLINE
    V AE2,ONLINE
  3. Update &SYSR2 system symbol definition

    Edit MVS PDS member, ADCD.Z24A.PARMLIB(IEASYM00).  Modify the definition of the &SYSR2 symbol to:

    SYMDEF(&SYSR2='&SYSR1(1:5).2')

    Save your changes.

    Now, whenever MVS is IPL’ed from RESVOL X4RES1 or Y4RES1, the system symbol, &SYSR2, will be set to X4RES2 or Y4RES2, respectively.

  4. Modify the catalog entries for RESVOL data sets

    Upload (using FTP in ASCII mode) the following REXX routine to a PDS on MVS:

    /* REXX */
    /* list all datasets on a volume */
    vol = ""
    newvol = ""
    arg vol newvol .
    if vol = '' | newvol = '' then ,
    exit xmsg('specify volume serial to change from/to', 12)

    say "Changing volser" vol "to" newvol "..."

    cc=bpxwdyn('alloc dd(sysin) new delete reuse',
    'lrecl(80) recfm(f,b) tracks space(1,1) unit(sysda)')
    if cc<>0 then exit xmsg('alloc sysin failed rc' cc)
    cc=bpxwdyn('alloc dd(sysprint) new delete reuse',
    'lrecl(121) recfm(f,b) tracks space(150,15) unit(sysda)')
    if cc<>0 then exit xmsg('alloc sysprint failed rc' cc)
    cc=bpxwdyn('alloc dd(disk) shr reuse',
    'unit(sysda) vol('vol')')
    if cc<>0 then exit xmsg('alloc disk failed rc' cc)
    rec.0 = 1
    rec.1 = ' LISTVTOC FORMAT,VOL=3390='vol
    "EXECIO 1 DISKW SYSIN (STEM rec. FINIS"

    "CALL *(IEHLIST)"

    "EXECIO * DISKR SYSPRINT (STEM lst. FINIS"
    found = 0
    nd = 0
    do n=1 to lst.0
    if POS("----DATA SET NAME----", lst.n) > 0 then do
    found = 1
    end
    else do
    if found then do
    nd = nd + 1
    parse var lst.n dsn.nd .
    found = 0
    end
    end
    end
    cc=bpxwdyn('free dd(sysin)')
    cc=bpxwdyn('free dd(sysprint)')
    cc=bpxwdyn('free dd(disk)')
    say "Total data sets: "nd
    dsn.0 = nd
    bad = 0
    do n = 1 to nd
    /* check that volser matches old value and get cattle dog */
    DROP idccmd.
    idccmd.0 = 3
    idccmd.1 = " LISTCAT -"
    idccmd.2 = " ENTRIES("dsn.n") -"
    idccmd.3 = " ALL NONVSAM"
    call Idcams
    oldvol = ""
    cat = ""
    do i = 1 to idcprt.0
    if POS("IN-CAT ---", idcprt.i) > 0 then do
    parse var idcprt.i . "IN-CAT --- "cat .
    end
    if POS("VOLSER------------", idcprt.i) > 0 then do
    parse var idcprt.i . "VOLSER------------"oldvol .
    end
    end
    if cat <> "" & oldvol = vol then do
    /* DELETE, DEFINE and LISTCAT */
    DROP idccmd.
    idccmd.0 = 12
    idccmd.1 = " DELETE -"
    idccmd.2 = " "dsn.n" -"
    idccmd.3 = " NOSCRATCH"
    idccmd.4 = " DEFINE NONVSAM ( -"
    idccmd.5 = " NAME("dsn.n") -"
    idccmd.6 = " DEVICETYPES(0000) -"
    idccmd.7 = " VOLUMES("newvol") -"
    idccmd.8 = " ) -"
    idccmd.9 = " CAT("cat")"
    idccmd.10 = " LISTCAT -"
    idccmd.11 = " ENTRIES("dsn.n") -"
    idccmd.12 = " ALL NONVSAM"
    Call Idcams
    oldvol = ""
    cat = ""
    do i = 1 to idcprt.0
    if POS("IN-CAT ---", idcprt.i) > 0 then do
    parse var idcprt.i . "IN-CAT --- "cat .
    end
    if POS("VOLSER------------", idcprt.i) > 0 then do
    parse var idcprt.i . "VOLSER------------"oldvol .
    end
    end
    say "Now:" dsn.n "VOL("oldvol") CAT("cat")"
    end
    else do
    say dsn.n
    say " Cat:>"cat"<"
    say " Vol:>"oldvol"<"
    say " *** Can't be recatalogued"
    bad = bad + 1
    baddsn.bad = dsn.n
    badcat.bad = cat
    badvol.bad = oldvol
    end
    end
    if bad > 0 then do
    say "The following" bad "of" nd "data sets could not be recatalogued:"
    do i = 1 to bad
    say baddsn.i "VOL("badvol.i") CAT("badcat.i")"
    end
    exit 0

    XMsg: if arg(1)<>'' then say arg(1);return word(arg(2) 0,1)

    Idcams:
    /* set stem variable idccmd. to commands to be executed */
    /* stem variable idcprt. contains results */
    DROP idcprt.
    idcprt.0 = 0
    cc=bpxwdyn('alloc dd(sysin) new delete reuse',
    'lrecl(80) recfm(f,b) tracks space(1,1) unit(sysda)')
    if cc<>0 then exit xmsg('alloc sysin failed rc' cc)
    cc=bpxwdyn('alloc dd(sysprint) new delete reuse',
    'lrecl(121) recfm(f,b) tracks space(150,15) unit(sysda)')
    if cc<>0 then exit xmsg('alloc sysprint failed rc' cc)

    "EXECIO * DISKW SYSIN (STEM idccmd. FINIS"

    "CALL *(IDCAMS)"

    "EXECIO * DISKR SYSPRINT (STEM idcprt. FINIS"
    cc=bpxwdyn('free dd(sysin)')
    cc=bpxwdyn('free dd(sysprint)')
    return

    From TSO, execute this REXX to change all references to the A4RES1 volume in the master catalog to ****** (which represents whichever primary RESVOL was used for the current IPL):

    EXEC 'ADCDMST.EXEC(RESRECAT)' 'A4RES1 ******'

    Repeat this process to change all references to A4RES2 to the volume contained in the &SYSR2 system symbol:

    EXEC 'ADCDMST.EXEC(RESRECAT)' 'A4RES2 &SYSR2'

    Note that it is normal that SYS1.VTOCIX.vol and SYS1.VVDS.vol  and around 20 other data sets on A4RES1 cannot be recatalogued.

    Re-IPL to check that the system still operates normally.

  5. Create jobs to copy "Gold" RESVOLs to the duplicate RESVOLs

    Create and submit (to test) JCL to copy the “gold” RESVOLs, A4RES1 and A4RES2, to each of the duplicate sets of RESVOLs:

    //RESCOPYX JOB REGION=0M,NOTIFY=&SYSUID,CLASS=A 
    //DO EXEC PGM=ADRDSSU,REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSIN DD *
    COPY FULL -
    INDYN((A4RES1)) -
    OUTDYN((X4RES1))
    COPY FULL -
    INDYN((A4RES2)) -
    OUTDYN((X4RES2))
    /*

    and

    //RESCOPYY JOB REGION=0M,NOTIFY=&SYSUID,CLASS=A 
    //DO EXEC PGM=ADRDSSU,REGION=0M
    //SYSPRINT DD SYSOUT=*
    //SYSIN DD *
    COPY FULL -
    INDYN((A4RES1)) -
    OUTDYN((Y4RES1))
    COPY FULL -
    INDYN((A4RES2)) -
    OUTDYN((Y4RES2))
    /*

    Reply to each “ADR369D AUTHORIZE FOR WRITE ACCESS A VTOCIX DATA SET ON x4RESn, RESCOPYx, DO , REPLY U OR T” message on the operator console with:

    nn,U

    Note that the following message, and a return code of 4 is normal for these jobs:

    IGD17054I DATA SET NOT FOUND FOR DELETE/RENAME ON VOLUME x4RESnDATA SET IS SYS1.VTOCIX.VA4RESn                                
  6. Modify DDDEFs for RESVOL data sets to point to "Gold" volumes

    Upload (using FTP in ASCII mode) the following REXX routine to a PDS on MVS:

    /* REXX */
    /* Alter DDDEF entries in SMP/E zone to specify
    explicit VOLUME and UNIT to bypass catalog */
    args = ""
    if ARG() > 0 then arg args
    if WORDS(args) < 3 then do
    say "Parms: csi zone resvol1 [resvol2 ...]"
    return
    end
    parse upper var args csi zone .
    nv = WORDS(args) - 2
    do i = 1 to nv
    vol.i = WORD(args, i+2)
    end
    say "CSI data set:" csi
    say "SMP/E zone: " zone
    say "RESVOLs:"
    do i = 1 to nv
    say " " vol.i
    end
    /* list DDDEFs in zone */
    DROP smpcmd.
    smpcmd.0 = 2
    smpcmd.1 = " SET BOUNDARY("zone")."
    smpcmd.2 = " LIST DDDEF."
    call Smpe
    ndd = 0
    ignore = 1
    do i = 1 to smpprt.0
    if LENGTH(smpprt.i) > 12 & ,
    SUBSTR(smpprt.i, 12, 1) <> " " & ,
    LEFT(smpprt.i, 11) = " " & ,
    ndd > 0 & ignore = 0 then do
    attr = ""
    val = ""
    parse var smpprt.i attr "=" val .
    attr = STRIP(attr)
    val = STRIP(val)
    /* drop UNIT and VOLUME because we will replace them */
    if attr <> "UNIT" & attr <> "VOLUME" & attr <> "" then do
    if val <> "" then do
    attrib.ndd = attrib.ndd attr"("val")"
    end
    else do
    attrib.ndd = attrib.ndd attr
    end
    end
    /* record if UNIT and VOLUME are already correct */
    if attr = "UNIT" & val = "3390" then hasunit.ndd = 1
    if attr = "VOLUME" then hasvol.ndd = val
    end
    else do
    if SUBSTR(smpprt.i, 12, 7) = "DATASET" then do
    ndd = ndd + 1
    parse var smpprt.i dddef.ndd "DATASET" "=" dsn.ndd .
    dddef.ndd = STRIP(dddef.ndd)
    dsn.ndd = STRIP(dsn.ndd)
    attrib.ndd = ""
    hasunit.ndd = 0
    hasvol.ndd = ""
    indd = 1
    ignore = 0
    end
    if SUBSTR(smpprt.i, 12, 7) = "CONCAT " | ,
    SUBSTR(smpprt.i, 12, 7) = "SYSOUT " | ,
    SUBSTR(smpprt.i, 12, 7) = "PATH " | ,
    SUBSTR(smpprt.i, 12, 7) = "SPACE " | ,
    SUBSTR(smpprt.i, 12, 7) = "SMPTLIB" then do
    say "Ignoring" smpprt.i
    ignore = 1
    end
    end
    end
    say "Total DDDEFs in zone" zone "=" ndd
    /* get data sets on RESVOLs */
    ndsn = 0
    do i = 1 to nv
    lvvol = vol.i
    call Listvtoc
    do j = 1 to lvdsn.0
    ndsn = ndsn + 1
    rdsn.ndsn = lvdsn.j
    rvol.ndsn = vol.i
    founddd.ndsn = 0
    end
    end
    say "Total datasets on RESVOLs =" ndsn
    /* match RESVOL datasets against DDDEFs and modify DDDEF for any match */
    do i = 1 to ndd
    found = 0
    j = 1
    do while found = 0 & j <= ndsn
    if dsn.i = rdsn.j then do
    if hasunit.i = 1 & hasvol.i = rvol.j then do
    say "Existing" dddef.i dsn.i attrib.i "UNIT(3390) VOLUME("rvol.j")"
    end
    else do
    say "Set" dddef.i "to" dsn.i attrib.i "UNIT(3390) VOLUME("rvol.j")"
    DROP smpcmd.
    smpcmd.0 = 7
    smpcmd.1 = " SET BOUNDARY("zone")."
    smpcmd.2 = " UCLIN."
    smpcmd.3 = " REP DDDEF("dddef.i")"
    smpcmd.4 = " DATASET("dsn.i")"
    smpcmd.5 = " "attrib.i
    smpcmd.6 = " UNIT(3390) VOLUME("rvol.j")."
    smpcmd.7 = " ENDUCL."
    call Smpe
    end
    founddd.j = 1
    found = 1
    end
    j = j + 1
    end
    end
    /* display RESVOL data sets for which no DDDEF exists in case */
    /* the DDDEF is defined in another zone or CSI */
    say "*** RESVOL data sets with no DDDEF in zone" zone":"
    do i = 1 to ndsn
    if founddd.i = 0 then do
    say rdsn.i "VOLUME("rvol.i")"
    end
    end
    say "*** End of List ***"
    return


    XMsg: if arg(1)<>'' then say arg(1);return word(arg(2) 0,1)

    Smpe:
    /* set stem variable smpcmd. to commands to be executed */
    /* stem variable smpprt. contains results */
    DROP smpprt.
    smpprt.0 = 0
    cc=bpxwdyn('alloc dd(smpcntl) new delete reuse',
    'lrecl(80) recfm(f,b) tracks space(1,1) unit(sysda)')
    if cc<>0 then exit xmsg('alloc smpcntl failed rc' cc)
    cc=bpxwdyn('alloc dd(smpcsi) shr reuse dsn('csi')')
    if cc<>0 then exit xmsg('alloc smpcsi failed rc' cc)
    cc=bpxwdyn('alloc dd(smplist) new delete reuse',
    'lrecl(121) recfm(f,b) tracks space(150,15) unit(sysda)')
    if cc<>0 then exit xmsg('alloc smplist failed rc' cc)
    cc=bpxwdyn('alloc dd(smplog) new delete reuse',
    'lrecl(121) recfm(f,b) tracks space(15,15) unit(sysda)')
    if cc<>0 then exit xmsg('alloc smplog failed rc' cc)
    cc=bpxwdyn('alloc dd(smpout) new delete reuse',
    'lrecl(121) recfm(f,b) tracks space(15,15) unit(sysda)')
    if cc<>0 then exit xmsg('alloc smpout failed rc' cc)
    cc=bpxwdyn('alloc dd(smprpt) new delete reuse',
    'lrecl(121) recfm(f,b) tracks space(15,15) unit(sysda)')
    if cc<>0 then exit xmsg('alloc smprpt failed rc' cc)

    "EXECIO * DISKW SMPCNTL (STEM smpcmd. FINIS"

    "CALL *(GIMSMP)"

    "EXECIO * DISKR SMPLIST (STEM smpprt. FINIS"
    cc=bpxwdyn('free dd(smpcntl)')
    cc=bpxwdyn('free dd(smpcsi)')
    cc=bpxwdyn('free dd(smplist)')
    cc=bpxwdyn('free dd(smplog)')
    cc=bpxwdyn('free dd(smpout)')
    cc=bpxwdyn('free dd(smprpt)')
    return

    Listvtoc: procedure expose lvvol lvdsn.
    /* set variable lvvol to volume to be listed */
    /* stem variable lvdsn. contains results */
    cc=bpxwdyn('alloc dd(sysin) new delete reuse',
    'lrecl(80) recfm(f,b) tracks space(1,1) unit(sysda)')
    if cc<>0 then exit xmsg('alloc sysin failed rc' cc)
    cc=bpxwdyn('alloc dd(sysprint) new delete reuse',
    'lrecl(121) recfm(f,b) tracks space(150,15) unit(sysda)')
    if cc<>0 then exit xmsg('alloc sysprint failed rc' cc)
    cc=bpxwdyn('alloc dd(disk) shr reuse',
    'unit(sysda) vol('lvvol')')
    if cc<>0 then exit xmsg('alloc disk failed rc' cc)
    rec.0 = 1
    rec.1 = ' LISTVTOC FORMAT,VOL=3390='lvvol
    "EXECIO 1 DISKW SYSIN (STEM rec. FINIS"

    "CALL *(IEHLIST)"

    DROP lst.
    "EXECIO * DISKR SYSPRINT (STEM lst. FINIS"

    found = 0
    nd = 0
    DROP lvdsn.
    do n=1 to lst.0
    if POS("----DATA SET NAME----", lst.n) > 0 then do
    found = 1
    end
    else do
    if found then do
    nd = nd + 1
    parse var lst.n lvdsn.nd .
    found = 0
    end
    end
    end
    cc=bpxwdyn('free dd(sysin)')
    cc=bpxwdyn('free dd(sysprint)')
    cc=bpxwdyn('free dd(disk)')
    lvdsn.0 = nd
    return

    From TSO, execute this REXX to change all DDDEFs in the MVST target zone to reference the A4RESn volumes explicitly:

    EXEC 'ADCDMST.EXEC(RESDDDEF)' 'MVS.GLOBAL.CSI MVST A4RES1 A4RES2'

    Note that some “orphan” data sets will be reported that exist on the A4RES1 or A4RES2 volumes but do not have a corresponding DDDEF in the MVST target zone.¬† This is normal.

  7. IPL from the "X" set of duplicate RESVOLs

    Shut down MVS.

    Re-IPL, specifying device 0AD1 as the IPL volume.  For example:

    ipl 0ad1 parm 0A82AL

    From now on, whenever you APPLY service to MVS, examine the FILE ALLOCATION REPORT provided by SMP/E.¬† If either of the “gold” RESVOLs has been touched by the service, run the RESCOPYx job to copy the “gold” RESVOLs to the “non-running” duplicate RESVOLs.¬†

    For example, if your current IPL loaded from X4RES1 (use operator command, D SYMBOLS, to check the current value of &SYSR1), then run job RESCOPYY to copy A4RES1 and A4RES2 to Y4RES1 and Y4RES2.  Then re-IPL your system from device 0ae1 (i.e. Y4RES1).

    Likewise, if the current IPL loaded from Y4RES1, then run job RESCOPYX and IPL from device 0ad1 (i.e. X4RES1).

  8. (optional) Automatically authorise volume copy operations

    Create (or edit, if it already exists) member ADCD.Z24A.PARMLIB(AUTOR00) and add the following lines to the file:

    NOTIFYMSGS(HC)
    /* AUTHORIZE A VOLUME COPY WHENEVER REQUESTED */
    MSGID(ADR369D) DELAY(1S) REPLY(U)

    Save the member.

    Activate this automatic message response by executing the following MVS operator command:

    SET AUTOR=00

    Now, whenever you copy the “gold” RESVOLs to one of the duplicate sets of RESVOLs (as described in step 5), the RESCOPYx job will run to completion without you having the authorise the volume copy operations.

Join The Discussion