ibm-information-center/dist/eclipse/plugins/i5OS.ic.rzaig_5.4.0.1/rzaigapplicationsqattsysc.htm

2624 lines
140 KiB
HTML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en-us" xml:lang="en-us">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="security" content="public" />
<meta name="Robots" content="index,follow" />
<meta http-equiv="PICS-Label" content='(PICS-1.1 "http://www.icra.org/ratingsv02.html" l gen true r (cz 1 lz 1 nz 1 oz 1 vz 1) "http://www.rsac.org/ratingsv01.html" l gen true r (n 0 s 0 v 0 l 0) "http://www.classify.org/safesurf/" l gen true r (SS~~000 1))' />
<meta name="DC.Type" content="concept" />
<meta name="DC.Title" content="Example: Application exit program" />
<meta name="abstract" content="This code example contains code for a sample application cluster resource group exit program." />
<meta name="description" content="This code example contains code for a sample application cluster resource group exit program." />
<meta name="DC.Relation" scheme="URI" content="rzaigapplicationscrg.htm" />
<meta name="copyright" content="(C) Copyright IBM Corporation 1998, 2006" />
<meta name="DC.Rights.Owner" content="(C) Copyright IBM Corporation 1998, 2006" />
<meta name="DC.Format" content="XHTML" />
<meta name="DC.Identifier" content="rzaigapplicationsqattsysc" />
<meta name="DC.Language" content="en-us" />
<!-- All rights reserved. Licensed Materials Property of IBM -->
<!-- US Government Users Restricted Rights -->
<!-- Use, duplication or disclosure restricted by -->
<!-- GSA ADP Schedule Contract with IBM Corp. -->
<link rel="stylesheet" type="text/css" href="./ibmdita.css" />
<link rel="stylesheet" type="text/css" href="./ic.css" />
<title>Example: Application exit program</title>
</head>
<body id="rzaigapplicationsqattsysc"><a name="rzaigapplicationsqattsysc"><!-- --></a>
<!-- Java sync-link --><script language="Javascript" src="../rzahg/synch.js" type="text/javascript"></script>
<h1 class="topictitle1">Example: Application exit program</h1>
<div><p>This code example contains code for a sample application cluster
resource group exit program.</p>
<p>You can find this code example in the QUSRTOOL library.</p>
<p>By using the code examples, you agree to the terms of the <a href="codedisclaimer.htm">Code
license and disclaimer information</a>.</p>
<pre>/***************************************************************************/
/* */
/* Library: QUSRTOOL */
/* File: QATTSYSC */
/* Member: TCSTAPPEXT */
/* Type: ILE C */
/* */
/* Description: */
/* This is an example application CRG exit program which gets called for */
/* various cluster events or cluster APIs. The bulk of the logic must */
/* still be added because that logic is really dependent upon the unique */
/* things that need to be done for a particular application. */
/* */
/* The intent of this example to to provide a shell which contains the */
/* basics for building a CRG exit program. Comments throughout the example*/
/* highlight the kinds of issues that need to be addressed by the real */
/* exit program implementation. */
/* */
/* Every action code that applies to an application CRG is handled in this */
/* example. */
/* */
/* The tcstdtaara.h include is also shipped in the QUSRTOOL library. See */
/* the TCSTDTAARA member in the QATTSYSC file. */
/* */
/* Change log: */
/* Flag Reason Ver Date User Id Description */
/* ____ ________ ______ ______ ___________________________________________ */
/* ... D98332 v5r1m0 000509 ROCH Initial creation. */
/* $A1 P9950070 v5r2m0 010710 ROCH Dataarea fixes */
/* $A2 D99055 v5r2m0 010913 ROCH Added CancelFailover action code */
/* $A3 D98854 v5r2m0 010913 ROCH Added VerificationPhase action code*/
/* $A4 P9A10488 v5r3m0 020524 ROCH Added example code to wait for data*/
/* CRGs on switchover action code */
/* */
/***************************************************************************/
/*-------------------------------------------------------------------------*/
/* */
/* Header files */
/* */
/*-------------------------------------------------------------------------*/
#include /* Useful when debugging */
#include /* offsetof macro */
#include /* system function */
#include /* String functions */
#include /* Exception handling constants/structures */
#include /* Various cluster constants */
#include /* Structure of CRG information */
#include "qusrtool/qattsysc/tcstdtaara" /* QCSTHAAPPI/QCSTHAAPPO data areas*/
#include /* API to Retrieve contents of a data area */
#include /* API error code type definition */
#include /* mitime builtin */
#include /* waittime builtin */
/*-------------------------------------------------------------------------*/
/* */
/* Constants */
/* */
/*-------------------------------------------------------------------------*/
#define UnknownRole -999
#define DependCrgDataArea "QCSTHAAPPO"
#define ApplCrgDataArea "QCSTHAAPPI"
#define Nulls 0x00000000000000000000
/*-------------------------------------------------------------------------*/
/* */
/* The following constants are used in the checkDependCrgDataArea() */
/* function. The first defines how long to sleep before checking the data */
/* area. The second defines that maximum time to wait for the data area */
/* to become ready before failing to start the application when the Start */
/* CRG function is being run. The third defines the maximum wait time for */
/* the Initiate Switchover or failover functions. */
/* */
/*-------------------------------------------------------------------------*/
#define WaitSecondsIncrement 30
#define MaxStartCrgWaitSeconds 0
#define MaxWaitSeconds 900
/*-------------------------------------------------------------------------*/
/* */
/* As this exit program is updated to handle new action codes, change the */
/* define below to the value of the highest numbered action code that is */
/* handled. */
/* */
/*-------------------------------------------------------------------------*/
#define MaxAc 21
/*-------------------------------------------------------------------------*/
/* */
/* If the exit program data in the CRG has a particular structure to it, */
/* include the header file for that structure definition and change the */
/* define below to use that structure name rather than char. */
/* */
/*-------------------------------------------------------------------------*/
#define EpData char
/*-------------------------------------------------------------------------*/
/* */
/* Change the following define to the library the application resides in */
/* and thus where the QCSTHAAPPO and QCSTHAAPPI data areas will be found. */
/* */
/*-------------------------------------------------------------------------*/
#define ApplLib "QGPL"
/*-------------------------------------------------------------------------*/
/* */
/* Prototypes for internal functions. */
/* */
/*-------------------------------------------------------------------------*/
static int getMyRole(Qcst_EXTP0100_t *, int, int);
#pragma argopt(getMyRole)
static int doAction(int, int, int, Qcst_EXTP0100_t *, EpData *);
#pragma argopt(doAction)
static int createCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int startCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int restartCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int endCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int verifyPhase(int, int, Qcst_EXTP0100_t *, EpData *);
static int deleteCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int memberIsJoining(int, int, Qcst_EXTP0100_t *, EpData *);
static int memberIsLeaving(int, int, Qcst_EXTP0100_t *, EpData *);
static int switchPrimary(int, int, Qcst_EXTP0100_t *, EpData *);
static int addNode(int, int, Qcst_EXTP0100_t *, EpData *);
static int rmvNode(int, int, Qcst_EXTP0100_t *, EpData *);
static int chgCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int deleteCrgWithCmd(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoPriorAction(int, int, Qcst_EXTP0100_t *, EpData *);
static int endNode(int, int, Qcst_EXTP0100_t *, EpData *);
static int chgNodeStatus(int, int, Qcst_EXTP0100_t *, EpData *);
static int cancelFailover(int, int, Qcst_EXTP0100_t *, EpData *);
static int newActionCode(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoCreateCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoStartCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoEndCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoMemberIsJoining(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoMemberIsLeaving(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoSwitchPrimary(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoAddNode(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoRmvNode(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoChgCrg(int, int, Qcst_EXTP0100_t *, EpData *);
static int undoCancelFailover(int, int, Qcst_EXTP0100_t *, EpData *);
static void bldDataAreaName(char *, char *, char *);
#pragma argopt(bldDataAreaName)
static int checkDependCrgDataArea(unsigned int);
#pragma argopt(checkDependCrgDataArea)
static void setApplCrgDataArea(char);
#pragma argopt(setApplCrgDataArea)
static void cancelHandler(_CNL_Hndlr_Parms_T *);
static void unexpectedExceptionHandler(_INTRPT_Hndlr_Parms_T *);
static void endApplication(unsigned int, int, int, Qcst_EXTP0100_t *, EpData *);
#pragma argopt(endApplication)
/*-------------------------------------------------------------------------*/
/* */
/* Some debug routines */
/* */
/*-------------------------------------------------------------------------*/
static void printParms(int, int, int, Qcst_EXTP0100_t *, EpData *);
static void printActionCode(unsigned int);
static void printCrgStatus(int);
static void printRcvyDomain(char *,
unsigned int,
Qcst_Rcvy_Domain_Array1_t *);
static void printStr(char *, char *, unsigned int);
/*-------------------------------------------------------------------------*/
/* */
/* Type definitions */
/* */
/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
/* */
/* This structure defines data that will be passed to the exception and */
/* cancel handlers. Extend it with information unique to your application.*/
/* */
/*-------------------------------------------------------------------------*/
typedef struct {
int *retCode; /* Pointer to return code */
EpData *epData; /* Exit program data from the CRG */
Qcst_EXTP0100_t *crgData; /* CRG data */
unsigned int actionCode; /* The action code */
int role; /* This node's recovery domain role */
int priorRole; /* This node's prior recovery domainrole */
} volatile HandlerDataT;
/*-------------------------------------------------------------------------*/
/* */
/* Function pointer array for handling action codes. When the exit program*/
/* is updated to handle new action codes, add the new function names to */
/* this function pointer array. */
/* */
/*-------------------------------------------------------------------------*/
static int (*fcn[MaxAc+1]) (int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) = {
newActionCode, /* 0 - currently reserved */
createCrg, /* 1 */
startCrg, /* 2 */
restartCrg, /* 3 */
endCrg, /* 4 */
verifyPhase, /* 5 - currently reserved */
newActionCode, /* 6 - currently reserved */
deleteCrg, /* 7 */
memberIsJoining, /* 8 */
memberIsLeaving, /* 9 */
switchPrimary, /* 10 */
addNode, /* 11 */
rmvNode, /* 12 */
chgCrg, /* 13 */
deleteCrgWithCmd,/* 14 */
undoPriorAction, /* 15 */
endNode, /* 16 */
newActionCode, /* 17 - applies only to a device CRG */
newActionCode, /* 18 - applies only to a device CRG */
newActionCode, /* 19 - applies only to a device CRG */
chgNodeStatus, /* 20 */
cancelFailover /* 21 */
};
/*-------------------------------------------------------------------------*/
/* */
/* Function pointer array for handling prior action codes when called with */
/* the Undo action code. When the exit program is updated to handle */
/* Undo for new action codes, add the new function names to this function */
/* pointer array. */
/* */
/*-------------------------------------------------------------------------*/
static int (*undoFcn[MaxAc+1]) (int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) = {
newActionCode, /* 0 - currently reserved */
undoCreateCrg, /* 1 */
undoStartCrg, /* 2 */
newActionCode, /* 3 */
undoEndCrg, /* 4 */
newActionCode, /* 5 - no undo for this action code */
newActionCode, /* 6 - currently reserved */
newActionCode, /* 7 */
undoMemberIsJoining, /* 8 */
undoMemberIsLeaving, /* 9 */
undoSwitchPrimary, /* 10 */
undoAddNode, /* 11 */
undoRmvNode, /* 12 */
undoChgCrg, /* 13 */
newActionCode, /* 14 */
newActionCode, /* 15 */
newActionCode, /* 16 */
newActionCode, /* 17 - applies only to a device CRG */
newActionCode, /* 18 - applies only to a device CRG */
newActionCode, /* 19 - applies only to a device CRG */
newActionCode, /* 20 */
undoCancelFailover /* 21 */
};
/***************************************************************************/
/* */
/* This is the entry point for the exit program. */
/* */
/***************************************************************************/
void main(int argc, char *argv[]) {
HandlerDataT hdlData;
/*-----------------------------------------------------------------------*/
/* */
/* Take each of the arguments passed in the argv array and castit to */
/* the correct data type. */
/* */
/*-----------------------------------------------------------------------*/
int *retCode = (int *)argv[1];
unsigned int *actionCode = (unsigned int *)argv[2];
EpData *epData = (EpData *)argv[3];
Qcst_EXTP0100_t *crgData = (Qcst_EXTP0100_t *)argv[4];
char *formatName = (char *)argv[5];
/*-----------------------------------------------------------------------*/
/* */
/* Ensure the format of the data being passed is what we areexpecting. */
/* If not, a change has been made and this exit program needs tobe */
/* updated to accomodate the change. Add appropriate errorlogging for */
/* your application design. */
/* */
/*-----------------------------------------------------------------------*/
if (0 != memcmp(formatName, "EXTP0100", 8))
abort();
/*-----------------------------------------------------------------------*/
/* */
/* Set up the data that will be passed to the exception andcancel */
/* handlers. */
/* */
/*-----------------------------------------------------------------------*/
hdlData.retCode = retCode;
hdlData.epData = epData;
hdlData.crgData = crgData;
hdlData.actionCode = *actionCode;
hdlData.role = UnknownRole;
hdlData.priorRole = UnknownRole;
_VBDY(); /* force changed variables to home storage location */
/*-----------------------------------------------------------------------*/
/* */
/* Enable an exception handler for any and all exceptions. */
/* */
/*-----------------------------------------------------------------------*/
#pragma exception_handler(unexpectedExceptionHandler, hdlData, \
_C1_ALL, _C2_ALL, _CTLA_INVOKE )
/*-----------------------------------------------------------------------*/
/* */
/* Enable a cancel handler to recover if this job is canceled. */
/* */
/*-----------------------------------------------------------------------*/
#pragma cancel_handler(cancelHandler, hdlData)
/*-----------------------------------------------------------------------*/
/* */
/* Extract the role and prior role of the node this exit program is */
/* running on. If the cluster API or event changes the recovery domain */
/* (node role or membership status), the new recovery domain's offset is */
/* passed in Offset_Rcvy_Domain_Array and the offset of the recovery */
/* domain as it looked prior to the API or cluster event is passed in */
/* Offset_Prior_Rcvy_Domain_Array. If the recovery domain isn't changed,*/
/* only Offset_Rcvy_Domain_Array can be used to address the recovery */
/* domain. */
/* */
/*-----------------------------------------------------------------------*/
hdlData.role = getMyRole(crgData,
crgData-&gt;Offset_Rcvy_Domain_Array,
crgData-&gt;Number_Nodes_Rcvy_Domain);
if (crgData-&gt;Offset_Prior_Rcvy_Domain_Array)
hdlData.priorRole =
getMyRole(crgData,
crgData-&gt;Offset_Prior_Rcvy_Domain_Array,
crgData-&gt;Number_Nodes_Prior_Rcvy_Domain);
else
hdlData.priorRole = hdlData.role;
_VBDY(); /* force changed variables to home storage location */
/*-----------------------------------------------------------------------*/
/* */
/* Enable the following to print out debug information. */
/* */
/*-----------------------------------------------------------------------*/
/*
printParms(*actionCode, hdlData.role, hdlData.priorRole, crgData,
epData);
*/
/*-----------------------------------------------------------------------*/
/* */
/* Do the correct thing based upon the action code. The return code */
/* is set to the function result of doAction(). */
/* */
/*-----------------------------------------------------------------------*/
*retCode = doAction(*actionCode,
hdlData.role,
hdlData.priorRole,
crgData,
epData);
/*-----------------------------------------------------------------------*/
/* */
/* The exit program job will end when control returns to the operating */
/* system at this point. */
/* */
/*-----------------------------------------------------------------------*/
return;
#pragma disable_handler /* unexpectedExceptionHandler */
#pragma disable_handler /* cancelHandler */
} /* end main() */
/***************************************************************************/
/* */
/* Get the role of this particular node from one of the views of the */
/* recovery domain. */
/* */
/* APIs and cluster events which pass the updated and prior recovery domain*/
/* to the exit program are: */
/* QcstAddNodeToRcvyDomain */
/* QcstChangeClusterNodeEntry */
/* QcstChangeClusterResourceGroup */
/* QcstEndClusterNode (ending node does not get the prior domain) */
/* QcstInitiateSwitchOver */
/* QcstRemoveClusterNodeEntry (removed node does not get the prior domain) */
/* QcstRemoveNodeFromRcvyDomain */
/* QcstStartClusterResourceGroup (only if inactive backup nodes are */
/* reordered) */
/* a failure causing failover */
/* a node rejoining the cluster */
/* cluster partitions merging */
/* */
/* All other APIs pass only the updated recovery domain. */
/* */
/***************************************************************************/
static int getMyRole(Qcst_EXTP0100_t *crgData, int offset, int
count) {
Qcst_Rcvy_Domain_Array1_t *nodeData;
unsigned int iter = 0;
/*-----------------------------------------------------------------------*/
/* */
/* Under some circumstances, the operating system may not be able to */
/* determine the ID of this node and passes *NONE. An example of such a */
/* circumstance is when cluster resource services is not active on a */
/* node and the DLTCRG CL command is used. */
/* */
/*-----------------------------------------------------------------------*/
if (0 == memcmp(crgData-&gt;This_Nodes_ID, QcstNone,
sizeof(Qcst_Node_Id_t)))
return UnknownRole;
/*-----------------------------------------------------------------------*/
/* */
/* Compute a pointer to the first element of the recovery domain array. */
/* */
/*-----------------------------------------------------------------------*/
nodeData = (Qcst_Rcvy_Domain_Array1_t *)((char *)crgData +
offset);
/*-----------------------------------------------------------------------*/
/* */
/* Find my node in the recovery domain array. I will not be in the */
/* prior recovery domain if I am being added by the Add Node to Recovery */
/* Domain API. */
/* */
/*-----------------------------------------------------------------------*/
while ( 0 != memcmp(crgData-&gt;This_Nodes_ID,
nodeData-&gt;Node_ID,
sizeof(Qcst_Node_Id_t))
&amp;&amp;
iter &amp;lt; count
) {
nodeData++;
iter++;
}
if (iter &amp;lt; count)
return nodeData-&gt;Node_Role;
else
return UnknownRole;
} /* end getMyRole() */
/***************************************************************************/
/* */
/* Call the correct function based upon the cluster action code. The */
/* doAction() function was split out from main() in order to clarify the */
/* example. See the function prologues for each called function for */
/* information about a particular cluster action. */
/* */
/* Each action code is split out into a separate function only to help */
/* clarify this example. For a particular exit program, some action codes */
/* may perform the same function in which case multiple action codes could */
/* be handled by the same function. */
/* */
/***************************************************************************/
static int doAction(int actionCode,
int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
/*-----------------------------------------------------------------------*/
/* */
/* For action codes this exit program knows about, call a function to */
/* do the work for that action code. */
/* */
/*-----------------------------------------------------------------------*/
if (actionCode &amp;lt;= MaxAc )
return (*fcn[actionCode]) (role, priorRole, crgData, epData);
else
/*---------------------------------------------------------------------*/
/* */
/* IBM has defined a new action code in a new operating system release */
/* and this exit program has not yet been updated to handle it. Take a*/
/* default action for now. */
/* */
/*---------------------------------------------------------------------*/
return newActionCode(role, priorRole, crgData, epData);
} /* end doAction() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcInitialize */
/* */
/* The QcstCreateClusterResourceGroup API was called. A new cluster */
/* resource group object is being created. */
/* */
/* Things to consider: */
/* - Check that the application program and all associated objects are on*/
/* the primary and backup nodes. If the objects are not there, */
/* consider sending error/warning messages or return a failure return */
/* code. */
/* - Check that required data or device CRGs are on all nodes in the */
/* recovery domain. */
/* - Perform any necessary setup that is required to run the */
/* the application on the primary or backup nodes. */
/* - If this CRG is enabled to use the QcstDistributeInformation API, */
/* the user queue needed by that API could be created at this time. */
/* */
/***************************************************************************/
static int createCrg(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstSuccessful;
} /* end createCrg() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcStart */
/* */
/* The QcstStartClusterResourceGroup API was called. A cluster resource */
/* group is being started. */
/* The QcstInitiateSwitchOver API was called and this is the second action */
/* code being passed to the exit program. */
/* The fail over event occurred and this is the second action code being */
/* passed to the exit program. */
/* */
/* A maximum wait time is used when checking to see if all dependent CRGs */
/* are active. This is a short time if the CRG is being started because of*/
/* the QcstStartClusterResourceGroup API. It is a longer time if it is */
/* because of a failover or switchover. When failover or switchover are */
/* being done, it make take a while for data or device CRGs to become */
/* ready so the wait time is long. If the Start CRG API is being used, the*/
/* dependent CRGs should already be started or some error occurred, the */
/* CRGs were started out of order, etc. and there is no need for a long */
/* wait. */
/* */
/* Things to consider: */
/* - If this node's role is primary, the application should be started. */
/* This exit program should either call the application so that it runs*/
/* in this same job or it should monitor any job started by this */
/* exit program so the exit program knows when the application job */
/* ends. By far, the simplest approach is run the application in this */
/* job by calling it. */
/* Cluster Resource Services is not expecting this exit program to */
/* return until the application finishes running. */
/* - If necessary, start any associated subsystems, server jobs, etc. */
/* - Ensure that required data CRGs have a status of active on all nodes */
/* in the recovery domain. */
/* */
/***************************************************************************/
static int startCrg(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
unsigned int maxWaitTime;
/* Start the application if this node is the primary */
if (role == QcstPrimaryNodeRole) {
/*---------------------------------------------------------------------*/
/* */
/* Determine if all CRGs that this application CRG is dependent upon */
/* are ready. If the check fails, return from the Start action code. */
/* Cluster Resource Services will change the state of the CRG to */
/* Inactive. */
/* */
/*---------------------------------------------------------------------*/
if (crgData-&gt;Cluster_Resource_Group_Status ==
QcstCrgStartCrgPending)
maxWaitTime = MaxStartCrgWaitSeconds;
else
maxWaitTime = MaxWaitSeconds;
if (QcstSuccessful != checkDependCrgDataArea(maxWaitTime))
return QcstSuccessful;
/*---------------------------------------------------------------------*/
/* */
/* Just before starting the application, update the data area to */
/* indicate the application is running. */
/* */
/*---------------------------------------------------------------------*/
setApplCrgDataArea(Appl_Running);
/*---------------------------------------------------------------------*/
/* */
/* Add logic to call application here. It is expected that control */
/* will not return until something causes the application to end: a */
/* normal return from the exit program, the job is canceled, or an */
/* unhandled exception occurs. See the cancelHandler() function for */
/* some common ways this job could be canceled. */
/* */
/*---------------------------------------------------------------------*/
/*---------------------------------------------------------------------*/
/* */
/* After the application has ended normally, update the data area to */
/* indicate the application is no longer running. */
/* */
/*---------------------------------------------------------------------*/
setApplCrgDataArea(Appl_Ended);
}
else
/*---------------------------------------------------------------------*/
/* */
/* On backup or replicate nodes, mark the status of the application in */
/* the data area as not running. */
/* */
/*---------------------------------------------------------------------*/
setApplCrgDataArea(Appl_Ended);
return QcstSuccessful;
} /* end startCrg()
*/
/***************************************************************************/
/* */
/* Action code = QcstCrgAcRestart */
/* */
/* The previous call of the exit program failed and set the return */
/* code to QcstFailWithRestart or it failed due to an exception and the */
/* exception was allowed to percolate up the call stack. In either */
/* case, the maximum number of times for restarting the exit program has */
/* not been reached yet. */
/* */
/* This action code is passed only to application CRG exit programs which */
/* had been called with the Start action code. */
/* */
/***************************************************************************/
static int restartCrg(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
/*-----------------------------------------------------------------------*/
/* */
/* Perform any unique logic that may be necessary when restarting the */
/* application after a failure and then call the startCrg() function to */
/* do the start functions. */
/* */
/*-----------------------------------------------------------------------*/
return startCrg(role, doesNotApply, crgData, epData);
} /* end restartCrg() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcEnd */
/* */
/* The end action code is used for one of the following reasons: */
/* - The QcstEndClusterResourceGroup API was called. */
/* - The cluster has become partitioned and this node is in the secondary*/
/* partition. The End action code is used regardless of whether the */
/* CRG was active or inactive. Action code dependent data of */
/* QcstPartitionFailure will also be passed. */
/* - The application ended. Action code dependent data of */
/* QcstResourceEnd will also be passed. All nodes in the recovery */
/* domain will see the same action code (including the primary). */
/* - The CRG job has been canceled. The exit program on this node will */
/* be called with the End action code. QcstMemberFailure will be */
/* passed as action code dependent data. */
/* */
/* */
/* */
/* Things to consider: */
/* - If the CRG is active, the job running the application is canceled */
/* and the IP takeover address is ended AFTER the exit program is */
/* called. */
/* - If subsystems or server jobs were started as a result of the */
/* QcstCrgAcStart action code, end them here or consolidate all logic */
/* to end the application in the cancelHandler() since it will be */
/* invoked for all Cluster Resource Services APIs which must end the */
/* application on the current primary. */
/* */
/***************************************************************************/
static int endCrg(int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
/*-----------------------------------------------------------------------*/
/* */
/* End the application if it is running on this node. */
/* */
/*-----------------------------------------------------------------------*/
endApplication(QcstCrgAcRemoveNode, role, priorRole, crgData,
epData);
return QcstSuccessful;
} /* end endCrg() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcVerificationPhase */
/* */
/* The verification phase action code is used to allow the exit program to */
/* do some verification before proceeding with the requested function */
/* identified by the action code depended data. If the exit program */
/* determines that the requested function cannot proceed it should return */
/* QcstFailWithOutRestart. */
/* */
/* */
/* NOTE: The exit program will NOT be called with Undo action code. */
/* */
/***************************************************************************/
static int verifyPhase(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
/*-----------------------------------------------------------------------*/
/* */
/* Do verification */
/* */
/*-----------------------------------------------------------------------*/
if (crgData-&gt;Action_Code_Dependent_Data == QcstDltCrg) {
/* do verification */
/* if ( fail ) */
/* return QcstFailWithOutRestart */
}
return QcstSuccessful;
} /* end verifyPhase() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcDelete */
/* */
/* The QcstDeleteClusterResourceGroup or QcstDeleteCluster API was called. */
/* A cluster resource group is being deleted while Cluster Resource */
/* Services is active. */
/* If the QcstDeleteCluster API was used, action code dependent data of */
/* QcstDltCluster is passed. */
/* If the QcstDeleteCluster API was used and the CRG is active, the exit */
/* program job which is still active for the Start action code is canceled*/
/* after the Delete action code is processed. */
/* */
/* Things to consider: */
/* - Delete application programs and objects from nodes where they are */
/* no longer needed such as backup nodes. Care needs to be exercised */
/* when deleting application objects just because a CRG is being */
/* deleted since a particular scenario may want to leave the */
/* application objects on all nodes. */
/* */
/***************************************************************************/
static int deleteCrg(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstSuccessful;
} /* end deleteCrg()
*/
/***************************************************************************/
/* */
/* Action code = QcstCrgAcReJoin */
/* */
/* One of three things is occurring- */
/* 1. The problem which caused the cluster to become partitioned has been */
/* corrected and the 2 partitions are merging back together to become */
/* a single cluster. Action code dependent data of QcstMerge will be */
/* passed. */
/* 2. A node which either previously failed or which was ended has had */
/* cluster resource services started again and the node is joining the */
/* cluster. Action code dependent data of QcstJoin will be passed. */
/* 3. The CRG job on a particular node which may have been canceled or */
/* ended has been restarted. Action code dependent data of QcstJoin */
/* will be passed. */
/* */
/* Things to consider: */
/* - If the application replicates application state information to other*/
/* nodes when the application is running, this state information will */
/* need to be resynchronized with the joining nodes if the CRG is */
/* active. */
/* - Check for missing application objects on the joining nodes. */
/* - Ensure the required data CRGs are on the joining nodes. */
/* - If the application CRG is active, ensure the required data CRGs are */
/* active. */
/* */
/***************************************************************************/
static int memberIsJoining(int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
/*---------------------------------------------------------------------*/
/* */
/* Ensure the data area status on this node starts out indicating */
/* the application is not running if this node is not the primary. */
/* */
/*---------------------------------------------------------------------*/
if (role != QcstPrimaryNodeRole) {
setApplCrgDataArea(Appl_Ended);
}
/*-----------------------------------------------------------------------*/
/* */
/* If a single node is rejoining the cluster, you may do a certain set of*/
/* actions. Whereas if the nodes in a cluster which became partitioned */
/* are merging back together, you may have a different set of actions. */
/* */
/*-----------------------------------------------------------------------*/
if (crgData-&gt;Action_Code_Dependent_Data == QcstJoin) {
/* Do actions for a node joining. */
}
else {
/* Do actions for partitions merging. */
}
return QcstSuccessful;
} /* end memberIsJoining() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcFailover */
/* */
/* Cluster resource services on a particular node(s) has failed or ended */
/* for this cluster resource group. The Failover action code is passed */
/* regardless of whether the CRG is active or inactive. Failover can */
/* happen for a number of reasons: */
/* */
/* - an operator canceled the CRG job on a node. Action code dependent */
/* data of QcstMemberFailure will be passed. */
/* - cluster resource services was ended on the node (for example, the */
/* QSYSWRK subsystem was ended with CRS still active). Action code */
/* dependent data of QcstNodeFailure will be passed. */
/* - the application for an application CRG has failed on the primary */
/* node and could not be restarted there. The CRG is Active. */
/* Action code dependent data of QcstApplFailure will be passed. */
/* - the node failed (such as a power failure). Action code dependent */
/* data of QcstNodeFailure will be passed. */
/* - The cluster has become partitioned due to some communication failure*/
/* such as a communication line or LAN failure. The Failover action */
/* code is passed to recovery domain nodes in the majority partition. */
/* Nodes in the minority partition see the End action code. Action */
/* code dependent data of QcstPartitionFailure will be passed. */
/* - A node in the CRG's recovery domain is being ended with the */
/* QcstEndClusterNode API. The node being ended will see the End Node */
/* action code. All other nodes in the recovery domain will see the */
/* Failover action code. Action code dependent data of QcstEndNode */
/* will be passed for the Failover action code. */
/* - An active recovery domain node for an active CRG is being removed */
/* from the cluster with the QcstRemoveClusterNodeEntry API. Action */
/* code dependent data of QcstRemoveNode will be passed. If an */
/* inactive node is removed for an active CRG, or if the CRG is */
/* inactive, an action code of Remove Node is passed. */
/* */
/* The exit program is called regardless of whether or not the CRG is */
/* active. The exit program may have nothing to do if the CRG is not */
/* active. */
/* */
/* If the CRG is active and the leaving member was the primary node, */
/* perform the functions necessary for failover to a new primary. */
/* */
/* The Action_Code_Dependent_Data field can be used to determine if: */
/* - the failure was due to a problem that caused the cluster to become */
/* partitioned (all CRGs which had the partitioned nodes in the */
/* recovery domain are affected) */
/* - a node failed or had cluster resource services ended on the node (all*/
/* CRGs which had the failed/ended node in the recovery domain are */
/* affected) */
/* - only a single CRG was affected (for example a single CRG job was */
/* canceled on a node or a single application failed) */
/* */
/* */
/* Things to consider: */
/* - Prepare the new primary node so the application can be started. */
/* - The application should NOT be started at this time. The exit */
/* program will be called again with the QcstCrgAcStart action code if */
/* the CRG was active when the failure occurred. */
/* - If the application CRG is active, ensure the required data CRGs are */
/* active. */
/* */
/***************************************************************************/
static int memberIsLeaving(int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
/*-----------------------------------------------------------------------*/
/* */
/* If the CRG is active, perform failover. Otherwise, nothing to do. */
/* */
/*-----------------------------------------------------------------------*/
if (crgData-&gt;Original_Cluster_Res_Grp_Stat == QcstCrgActive) {
/*---------------------------------------------------------------------*/
/* */
/* The CRG is active. Determine if my role has changed and I am now */
/* the new primary. */
/* */
/*---------------------------------------------------------------------*/
if (priorRole != role &amp;&amp; role == QcstPrimaryNodeRole) {
/*-------------------------------------------------------------------*/
/* */
/* I was not the primary but am now. Do failover actions but don't */
/* start the application at this time because this exit program will */
/* be called again with the Start action code. */
/* */
/*-------------------------------------------------------------------*/
/*-------------------------------------------------------------------*/
/* */
/* Ensure the data area status on this node starts out indicating */
/* the application is not running. */
/* */
/*-------------------------------------------------------------------*/
setApplCrgDataArea(Appl_Ended);
/*-------------------------------------------------------------------*/
/* */
/* If the application has no actions to do on the Start action code */
/* and will become active as soon as the takeover IP address is */
/* activated, then this code should be uncommented. This code will */
/* determine if all CRGs that this application CRG is dependent upon */
/* are ready. If this check fails, return failure from the action */
/* code. */
/* */
/*-------------------------------------------------------------------*/
/* if (QcstSuccessful != checkDependCrgDataArea(MaxWaitSeconds)) */
/* return QcstFailWithOutRestart; */
}
}
return QcstSuccessful;
} /* end memberIsLeaving() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcSwitchover */
/* */
/* The QcstInitiateSwitchOver API was called. The first backup node in */
/* the cluster resource group's recovery domain is taking over as the */
/* primary node and the current primary node is being made the last backup.*/
/* */
/* Things to consider: */
/* - Prepare the new primary node so the application can be started. */
/* - The application should NOT be started at this time. The exit */
/* program will be called again with the QcstCrgAcStart action code. */
/* - The job running the application is canceled and the IP takeover */
/* address is ended prior to the exit program being called on the */
/* current primary. */
/* - Ensure required data or device CRGs have switched over and are */
/* active. */
/* */
/***************************************************************************/
static int switchPrimary(int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
/*-----------------------------------------------------------------------*/
/* */
/* See if I am the old primary. */
/* */
/*-----------------------------------------------------------------------*/
if (priorRole == QcstPrimaryNodeRole) {
/*---------------------------------------------------------------------*/
/* */
/* Do what ever needs to be done to cleanup the old primary before the */
/* switch. Remember that that job which was running the exit program */
/* which started the application was canceled already. */
/* */
/* One example may be to clean up any processes holding locks on the */
/* database. This may have been done by the application cancel */
/* handler if one was invoked. */
/*---------------------------------------------------------------------*/
}
/*-----------------------------------------------------------------------*/
/* */
/* I'm not the old primary. See if I'm the new primary. */
/* */
/*-----------------------------------------------------------------------*/
else if (role == QcstPrimaryNodeRole) {
/*---------------------------------------------------------------------*/
/* */
/* Do what ever needs to be done on the new primary before the */
/* application is started with the QcstCrgAcStart action code. */
/* */
/*---------------------------------------------------------------------*/
/*---------------------------------------------------------------------*/
/* */
/* Ensure the data area status on this nodes starts out indicating */
/* the application is not running. */
/* */
/*---------------------------------------------------------------------*/
setApplCrgDataArea(Appl_Ended);
/*---------------------------------------------------------------------*/
/* */
/* If the application has no actions to do on the Start action code */
/* and will become active as soon as the takeover IP address is */
/* activated, then this code should be uncommented. This code will */
/* determine if all CRGs that this application CRG is dependent upon */
/* are ready. If this check fails, return failure from the action */
/* code. */
/* */
/*---------------------------------------------------------------------*/
/* if (QcstSuccessful != checkDependCrgDataArea(MaxWaitSeconds)) */
/* return QcstFailWithOutRestart; */
}
else {
/*---------------------------------------------------------------------*/
/* */
/* This node is one of the other backup nodes or it is a replicate */
/* node. If there is anything those nodes must do, do it here. If */
/* not, remove this else block. */
/* */
/*---------------------------------------------------------------------*/
/*---------------------------------------------------------------------*/
/* */
/* Ensure the data area status on this nodes starts out indicating */
/* the application is not running. */
/* */
/*---------------------------------------------------------------------*/
setApplCrgDataArea(Appl_Ended);
}
return QcstSuccessful;
} /* end switchPrimary() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcAddNode */
/* */
/* The QcstAddNodeToRcvyDomain API was called. A new node is being added */
/* to the recovery domain of a cluster resource group. */
/* */
/* Things to consider: */
/* - A new node is being added to the recovery domain. See the */
/* considerations in the createCrg() function. */
/* - If this CRG is enabled to use the QcstDistributeInformation API, */
/* the user queue needed by that API could be created at this time. */
/* */
/***************************************************************************/
static int addNode(int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
/*-----------------------------------------------------------------------*/
/* */
/* Determine if I am the node being added. */
/* */
/*-----------------------------------------------------------------------*/
if (0 == memcmp(&amp;crgData-&gt;This_Nodes_ID,
&amp;crgData-&gt;Changing_Node_ID,
sizeof(Qcst_Node_Id_t)))
{
/*---------------------------------------------------------------------*/
/* */
/* Set the status of the data area on this new node. */
/* */
/*---------------------------------------------------------------------*/
setApplCrgDataArea(Appl_Ended);
/*---------------------------------------------------------------------*/
/* */
/* Create the queue needed by the Distribute Information API. */
/* */
/*---------------------------------------------------------------------*/
if (0 == memcmp(&amp;crgData-&gt;DI_Queue_Name,
Nulls,
sizeof(crgData-&gt;DI_Queue_Name)))
{
}
}
return QcstSuccessful;
} /* end addNode()
*/
/***************************************************************************/
/* */
/* Action code = QcstCrgAcRemoveNode */
/* */
/* The QcstRemoveNodeFromRcvyDomain or the QcstRemoveClusterNodeEntry */
/* API was called. A node is being removed from the recovery domain of */
/* a cluster resource group or it is being removed entirely from the */
/* cluster. */
/* */
/* This action code is seen by: */
/* For the QcstRemoveClusterNodeEntry API: */
/* - If the removed node is active and the CRG is Inactive, all nodes in*/
/* the recovery domain including the node being removed see this */
/* action code. The nodes NOT being removed see action code dependent*/
/* data of QcstNodeFailure. */
/* - If the removed node is active and the CRG is Active, the node being*/
/* removed sees the Remove Node action code. All other nodes in the */
/* recovery domain see an action code of Failover and action code */
/* dependent data of QcstNodeFailure. */
/* - If the node being removed is not active in the cluster, all nodes */
/* in the recovery domain will see this action code. */
/* For the QcstRemoveNodeFromRcvyDomain API: */
/* - All nodes see the Remove Node action code regardless of whether or */
/* not the CRG is Active. Action code dependent data of */
/* QcstRmvRcvyDmnNode will also be passed. */
/* */
/* Things to consider: */
/* - You may want to cleanup the removed node by deleting objects no */
/* longer needed there. */
/* - The job running the application is canceled and the IP takeover */
/* address is ended after the exit program is called if this is the */
/* primary node and the CRG is active. */
/* - If subsystems or server jobs were started as a result of the */
/* QcstCrgAcStart action code, end them here or consolidate all logic */
/* to end the application in the cancelHandler() since it will be */
/* invoked for all Cluster Resource Services APIs which must end the */
/* application on the current primary. */
/* */
/***************************************************************************/
static int rmvNode(int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
/*-----------------------------------------------------------------------*/
/* */
/* Determine if I am the node being removed. */
/* */
/*-----------------------------------------------------------------------*/
if (0 == memcmp(&amp;crgData-&gt;This_Nodes_ID,
&amp;crgData-&gt;Changing_Node_ID,
sizeof(Qcst_Node_Id_t)))
{
/*-------------------------------------------------------------------*/
/* */
/* End the application if it is running on this node. */
/* */
/*-------------------------------------------------------------------*/
endApplication(QcstCrgAcRemoveNode, role, priorRole, crgData,
epData);
}
return QcstSuccessful;
} /* end rmvNode */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcChange */
/* */
/* The QcstChangeClusterResourceGroup API was called. Some attribute */
/* or information stored in the cluster resource group object is being */
/* changed. Note that not all changes to the CRG object cause the exit */
/* program to be called. As of V5R1M0, only these changes will cause the */
/* exit program to be called- */
/* - the current recovery domain is being changed */
/* - the preferred recovery domain is being changed */
/* */
/* If any of the above changes are being made but additionally the exit */
/* program is being changed to *NONE, the exit program is not called. */
/* */
/* Things to consider: */
/* - None unless changing the recovery domain affects information or */
/* processes for this cluster resource group. Note that the primary */
/* node cannot be changed with the QcstChangeClusterResourceGroup API */
/* if the CRG is active. */
/* */
/***************************************************************************/
static int chgCrg(int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstSuccessful;
} /* end chgCrg() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcDeleteCommand */
/* */
/* The Delete Cluster Resource Group (DLTCRG) CL command has been called */
/* to delete a cluster resource group object, the QcstDeleteCluster API */
/* has been called, or the QcstRemoveClusterNodeEntry API has been called. */
/* In each case, cluster resource services is not active on the cluster */
/* node where the command or API was called. Thus, this function is not */
/* distributed cluster wide but occurs only on the node where the CL */
/* command or API was called. */
/* */
/* If the QcstDeleteCluster API was used, action code dependent data of */
/* QcstDltCluster is passed. */
/* */
/* See the considerations in the deleteCrg() function */
/* */
/***************************************************************************/
static int deleteCrgWithCmd(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstSuccessful;
} /* end deleteCrgWithCmd() */
/***************************************************************************/
/* */
/* Action code = QcstCrgEndNode */
/* */
/* The QcstEndClusterNode API was called or a CRG job was canceled. */
/* */
/* The QcstCrgEndNode action code is passed to the exit program only on the*/
/* node being ended or where the CRG job was canceled. On the node where */
/* a Cluster Resource Services job is canceled, action code dependent data*/
/* of QcstMemberFailure will be passed. */
/* When Cluster Resource Services ends on this node or the CRG job ends, it*/
/* will cause all other nodes in the cluster to go through failover */
/* processing. The action code passed to all other nodes will be */
/* QcstCrgAcFailover. Those nodes will see action code dependent data of */
/* QcstMemberFailure if a CRG job is canceled or QcstNodeFailure if the */
/* node is ended. */
/* */
/* Things to consider: */
/* - The job running the application is canceled and the IP takeover */
/* address is ended after the exit program is called if this is the */
/* primary node and the CRG is active. */
/* - If subsystems or server jobs were started as a result of the */
/* QcstCrgAcStart action code, end them here. */
/* */
/***************************************************************************/
static int endNode(int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
/*-----------------------------------------------------------------------*/
/* */
/* End the application if it is running on this node. */
/* */
/*-----------------------------------------------------------------------*/
endApplication(QcstCrgEndNode, role, priorRole, crgData, epData);
return QcstSuccessful;
} /* end endNode() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcChgNodeStatus */
/* */
/* The QcstChangeClusterNodeEntry API was called. The status of a node */
/* is being changed to failed. This API is used to inform cluster resource*/
/* services that the node did not partition but really failed. */
/* */
/* Things to consider: */
/* - The exit program was called previously with an action code of */
/* QcstCrgAcEnd if the CRG was active or an action code of */
/* QcstCrgAcFailover if the CRG was inactive because cluster resource */
/* services thought the cluster had become partitioned. The user is */
/* now telling cluster resource services that the node really failed */
/* instead of partitioned. The exit program has something to do only */
/* if it performed some action previously that needs to be changed now */
/* that node failure can be confirmed. */
/* */
/***************************************************************************/
static int chgNodeStatus(int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstSuccessful;
} /* end chgNodeStatus() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcCancelFailover */
/* */
/* Cluster resource services on the primary node has failed or ended */
/* for this cluster resource group. A message was sent to the failover */
/* message queue specified for the CRG, and the result of that message */
/* was to cancel the failover. This will change the status of the CRG to */
/* inactive and leave the primary node as primary. */
/* */
/* Things to consider: */
/* - The primary node is no longer participating in cluster activities. */
/* The problem which caused the primary node to fail should be fixed */
/* so that the CRG may be started again. */
/* */
/***************************************************************************/
static int cancelFailover(int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstSuccessful;
} /* end cancelFailover() */
/***************************************************************************/
/* */
/* Action code = exit program does not know it yet */
/* */
/* A new action code has been passed to this exit program. This can occur */
/* after a new i5/OS release has been installed and some new cluster API */
/* was called or some new cluster event occurred. The logic in this exit */
/* program has not yet been updated to understand the new action code. */
/* */
/* Two different strategies could be used for the new action code. The */
/* correct strategy is dependent upon the kinds of things this particular */
/* exit program does for the application. */
/* */
/* One strategy is to not do anything and return a successful return code. */
/* This allows the new cluster API or event to run to completion. It */
/* allows the function to be performed even though this exit program */
/* did not understand the new action code. The risk, though, is that the */
/* exit program should have done something and it did not. At a minimum, */
/* you may want to log some kind of error message about what happened so */
/* that programming can investigate and get the exit program updated. */
/* */
/* The opposite strategy is to return an error return code such as */
/* QcstFailWithRestart. Of course doing this means that the new cluster */
/* API or event cannot be used until the exit program is updated for the */
/* new action code. Again, logging some kind of error message for */
/* programming to investigate would be worthwhile. */
/* */
/* Only the designer of the exit program can really decide which is the */
/* better course of action. */
/* */
/***************************************************************************/
static int newActionCode(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
/*-----------------------------------------------------------------------*/
/* */
/* Add logic to log an error somewhere - operator message queue, job */
/* log, application specific error log, etc. so that the exit program */
/* gets updated to properly handle the new action code. */
/* */
/* Note that if this is left coded as it is, this is the "don't do */
/* anything" strategy described in the prologue above. */
/* */
/*-----------------------------------------------------------------------*/
return QcstSuccessful;
} /* end newActionCode() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcUndo */
/* */
/* Note: The exit program is never called with an undo action code for */
/* any of these prior action codes: */
/* QcstCrgAcChgNodeStatus */
/* QcstCrgAcDelete */
/* QcstCrgAcDeleteCommand */
/* QcstCrgEndNode */
/* QstCrgAcRemoveNode (If the node being removed is active in the */
/* cluster and the API is Remove Cluster Node. */
/* The Remove Node From Recovery Domain will call */
/* with Undo and the Remove Cluster Node API will */
/* call with Undo if the node being removed is */
/* inactive. */
/* QcstCrgAcRestart */
/* QcstCrgAcUndo */
/* */
/* APIs that call an exit program do things in 3 steps. */
/* 1. Logic which must be done prior to calling the exit program. */
/* 2. Call the exit program. */
/* 3. Logic which must be done after calling the exit program. */
/* */
/* Any errors that occur during steps 2 or 3 result in the exit program */
/* being called again with the undo action code. This gives the exit */
/* program an opportunity to back out any work performed when it was first */
/* called by the API. The API will also be backing out any work it */
/* performed trying to return the state of the cluster and cluster objects */
/* to what it was before the API was called. */
/* */
/* It is suggested that the following return codes be returned for the */
/* specified action code as that return code will result in the most */
/* appropriate action being taken. */
/* */
/* QcstCrgAcInitialize: QcstSuccessful; The CRG is not created. */
/* QcstCrgAcStart: QcstSuccessful; The CRG is not started. */
/* QcstCrgAcEnd: QcstFailWithOutRestart; The CRG is set to Indoubt*/
/* The cause of the failure needs to*/
/* investigated. */
/* QcstCrgAcReJoin: QcstFailWithOutRestart; The CRG is set to Indoubt*/
/* The cause of the failure needs to*/
/* investigated. */
/* QcstCrgAcFailover: QcstFailWithOutRestart; The CRG is set to Indoubt*/
/* The cause of the failure needs to*/
/* investigated. */
/* QcstCrgAcSwitchover: QcstFailWithOutRestart; The CRG is set to Indoubt*/
/* The cause of the failure needs to*/
/* investigated. */
/* QcstCrgAcAddNode: QcstSuccessful; The node is not added. */
/* QcstCrgAcRemoveNode: QcstFailWithOutRestart; The CRG is set to Indoubt*/
/* The cause of the failure needs to*/
/* investigated. */
/* QcstCrgAcChange: QcstSuccessful; The recovery domain is not */
/* changed. */
/* */
/***************************************************************************/
static int undoPriorAction(int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
/*-----------------------------------------------------------------------*/
/* */
/* The prior action code defines what the exit program was doing when */
/* it failed, was canceled, or returned a non successful return code. */
/* */
/*-----------------------------------------------------------------------*/
if (crgData-&gt;Prior_Action_Code &amp;lt;= MaxAc )
return (*undoFcn[crgData-&amp;lt;Prior_Action_Code])
(role, priorRole, crgData,
epData);
else
/*---------------------------------------------------------------------*/
/* */
/* IBM has defined a new action code in a new operating system release */
/* and this exit program has not yet been updated to handle it. Take a*/
/* default action for now. */
/* */
/*---------------------------------------------------------------------*/
return newActionCode(role, priorRole, crgData, epData);
} /* end undoPriorAction() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcUndo */
/* */
/* Prior action code = QcstCrgAcInitialize */
/* */
/* Things to consider: */
/* The CRG will not be created. Objects that might have been created */
/* on nodes in the recovery domain should be deleted since a subsequent */
/* create could fail if those objects already exist. */
/* */
/***************************************************************************/
static int undoCreateCrg(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstSuccessful;
} /* end undoCreateCrg() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcUndo */
/* */
/* Prior action code = QcstCrgAcStart */
/* */
/* Things to consider: */
/* Cluster Resource Services failed when it was finishing the Start CRG */
/* API after it had already called the exit program with the Start */
/* Action code. */
/* */
/* On the primary node, the exit program job which is running the */
/* application will be canceled. The exit program will then be called */
/* with the Undo action code. */
/* */
/* All other nodes in the recovery domain will be called with the Undo */
/* action code. */
/* */
/***************************************************************************/
static int undoStartCrg(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstSuccessful;
} /* end undoStartCrg() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcUndo */
/* */
/* Prior action code = QcstCrgAcEnd */
/* */
/* Things to consider: */
/* The CRG will not be ended. If the exit program did anything to bring */
/* down the application it can either restart the application or it can */
/* decide to not restart the application. If the application is not */
/* restarted, the return code should be set to QcstFailWithOutRestart so */
/* the status of the CRG is set to Indoubt. */
/* */
/***************************************************************************/
static int undoEndCrg(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstFailWithOutRestart;
} /* end undoEndCrg() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcUndo */
/* */
/* Prior action code = QcstCrgAcReJoin */
/* */
/* Things to consider: */
/* An error occurred which won't allow the member to join this CRG */
/* group. Anything done for the Join action code needs to be looked at */
/* to see if something must be undone if this member is not an active */
/* member of the CRG group. */
/* */
/***************************************************************************/
static int undoMemberIsJoining(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstFailWithOutRestart;
} /* end undoMemberIsJoining() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcUndo */
/* */
/* Prior action code = QcstCrgAcFailover */
/* */
/* Things to consider: */
/* This does not mean that the node failure or failing member is being */
/* undone. That failure is irreversible. What it does mean is that the */
/* exit program returned an error from the Failover action code or */
/* Cluster Resource Services ran into a problem after it called the exit */
/* program. If the CRG was active when Failover was attempted, it is */
/* not at this point. End the resilient resource and expect a human to */
/* look into the failure. After the failure is corrected, the CRG will */
/* must be started with the Start CRG API. */
/* */
/* */
/***************************************************************************/
static int undoMemberIsLeaving(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstFailWithOutRestart;
} /* end undoMemberIsLeaving() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcUndo */
/* */
/* Prior action code = QcstCrgAcSwitchover */
/* */
/* Things to consider: */
/* Some error occurred after the point of access was moved from the */
/* original primary and before it could be brought up on the new primary.*/
/* The IP address was ended on the original primary before moving the */
/* point of access but is started on the original primary again. Cluster*/
/* Resource Services will now attempt to move the point of access back */
/* to the original primary. The application exit program and IP takeover*/
/* address will be started on the original primary. */
/* */
/* */
/***************************************************************************/
static int undoSwitchPrimary(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstFailWithOutRestart;
} /* end undoSwitchPrimary() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcUndo */
/* */
/* Prior action code = QcstCrgAcAddNode */
/* */
/* Things to consider: */
/* If objects were created on the new node, they should be removed so */
/* that a subsequent Add Node to aRecovery Domain does not fail if it */
/* attempts to create objects again. */
/* */
/* */
/***************************************************************************/
static int undoAddNode(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstSuccessful;
} /* end undoAddNode() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcUndo */
/* */
/* Prior action code = QcstCrgAcRemoveNode */
/* */
/* Things to consider: */
/* The node is still in the recovery domain. If objects were removed */
/* from the node, they should be added back. */
/* */
/***************************************************************************/
static int undoRmvNode(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstFailWithOutRestart;
} /* end undoRmvNode() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcUndo */
/* */
/* Prior action code = QcstCrgAcChange */
/* */
/* Things to consider: */
/* Changes to the CRG will be backed out so that the CRG and its */
/* recovery domain look just like it did prior to the attempted change. */
/* Any changes the exit program made should also be backed out. */
/* */
/***************************************************************************/
static int undoChgCrg(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstSuccessful;
} /* end undoChgCrg() */
/***************************************************************************/
/* */
/* Action code = QcstCrgAcUndo */
/* */
/* Prior action code = QcstCrgAcCancelFailover */
/* */
/* Things to consider: */
/* This does not mean that the node failure or failing member is being */
/* undone. That failure is irreversible. What it does mean is that */
/* Cluster Resource Services ran into a problem after it called the exit */
/* program. The CRG will be InDoubt regardless of what is returned from */
/* this exit program call. Someone will need to manually look into the */
/* the failure. After the failure is corrected, the CRG will must be */
/* started with the Start CRG API. */
/* */
/* */
/***************************************************************************/
static int undoCancelFailover(int role,
int doesNotApply,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
return QcstSuccessful;
} /* end undoCancelFailover() */
/***************************************************************************/
/* */
/* A simple routine to take a null terminated object name and a null */
/* terminated library name and build a 20 character non-null terminated */
/* qualified name. */
/* */
/***************************************************************************/
static void bldDataAreaName(char *objName, char* libName, char *qualName) {
memset(qualName, 0x40, 20);
memcpy(qualName, objName, strlen(objName));
qualName += 10;
memcpy(qualName, libName, strlen(libName));
return;
} /* end bldDataAreaName */
/***************************************************************************/
/* */
/* The data area is checked to see if all the CRGs that this application */
/* is dependent upon are ready. If they are not ready, a wait for a */
/* certain amount of time is performed and the data area is checked again. */
/* This check, wait loop continues until all dependent CRGs become ready or*/
/* until the maximum wait time has been reached. */
/* The length of the wait can be changed to some other value if a */
/* particular situation would be better with shorter or longer wait times. */
/* */
/* */
/***************************************************************************/
static int checkDependCrgDataArea(unsigned int maxWaitTime) {
Qus_EC_t errCode = { sizeof(Qus_EC_t), 0 };
char dataAreaName[20];
struct {
Qwc_Rdtaa_Data_Returned_t stuff;
char ready;
} data;
/*-----------------------------------------------------------------------*/
/* */
/* This is an accumulation of the time waited for the dependent CRGs to */
/* become ready. */
/* */
/*-----------------------------------------------------------------------*/
unsigned int timeWaited = 0;
/*-----------------------------------------------------------------------*/
/* */
/* Build definition of the amount of time to wait. */
/* */
/*-----------------------------------------------------------------------*/
_MI_Time timeToWait;
int hours = 0;
int minutes = 0;
int seconds = WaitSecondsIncrement;
int hundreths = 0;
short int options = _WAIT_NORMAL;
mitime( &amp;timeToWait, hours, minutes, seconds, hundreths );
/*-----------------------------------------------------------------------*/
/* */
/* Build the qualified name of the data area. */
/* */
/*-----------------------------------------------------------------------*/
bldDataAreaName(DependCrgDataArea, ApplLib, dataAreaName);
/*-----------------------------------------------------------------------*/
/* */
/* Get the data from the data area that indicates whether or not the */
/* CRGs are all ready. This data area is updated by the High */
/* Availability Business Partners when it is ok for the application to */
/* proceed. */
/* */
/*-----------------------------------------------------------------------*/
QWCRDTAA(&amp;data,
sizeof(data),
dataAreaName,
offsetof(Qcst_HAAPPO_t,Data_Status)+1, /* API wants a 1 origin */
sizeof(data.ready),
&amp;errCode);
/*-----------------------------------------------------------------------*/
/* */
/* If the dependent CRGs are not ready, wait for a bit and check again. */
/* */
/*-----------------------------------------------------------------------*/
while (data.ready != Data_Available) {
/*---------------------------------------------------------------------*/
/* */
/* If the dependent CRGs have not become ready by the time we have */
/* waited our maximum wait time, return an error. Consider logging */
/* some message to describe why the application did not start so that */
/* the problem can be looked into. */
/* */
/*---------------------------------------------------------------------*/
if (timeWaited &gt;= maxWaitTime)
return QcstFailWithOutRestart;
/*---------------------------------------------------------------------*/
/* */
/* Wait to allow the data CRGs to become ready. */
/* */
/*---------------------------------------------------------------------*/
waittime(&amp;timeToWait, options);
timeWaited += WaitSecondsIncrement;
/*---------------------------------------------------------------------*/
/* */
/* Get information from the data area again to see if the data CRGs are*/
/* ready. */
/* */
/*---------------------------------------------------------------------*/
QWCRDTAA(&amp;data,
sizeof(data),
dataAreaName,
offsetof(Qcst_HAAPPO_t,Data_Status)+1, /* API wants a 1 origin */
sizeof(data.ready),
&amp;errCode);
}
return QcstSuccessful;
} /* end checkDependCrgDataArea */
/***************************************************************************/
/* */
/* The application CRG data area is updated to indicate that the */
/* application is running or to indicate it is not running. This data area*/
/* information is used by the High Availability Business Partners to */
/* coordinate the switchover activities between CRGs that have dependencies*/
/* on each other. */
/* */
/***************************************************************************/
static void setApplCrgDataArea(char status) {
char cmd[54];
char cmdEnd[3] = {0x00, ')', 0x00};
/*-----------------------------------------------------------------------*/
/* */
/* Set up the CL command string with the data area library name, the data*/
/* area name, and the character to put into the data area. Then run the */
/* CL command. */
/* */
/*-----------------------------------------------------------------------*/
memcpy(cmd, "CHGDTAARA DTAARA(", strlen("CHGDTAARA DTAARA(")+1);
strcat(cmd, ApplLib);
strcat(cmd, "/");
strcat(cmd, ApplCrgDataArea);
strcat(cmd, " (425 1)) VALUE("); /* @A1C */
cmdEnd[0] = status;
strcat(cmd, cmdEnd);
system(cmd);
return;
} /* end setApplCrgDataArea */
/***************************************************************************/
/* */
/* This function is called any time the exit program receives an exception */
/* not specifically monitored for by some other exception handler. Add */
/* appropriate logic to perform cleanup functions that may be required. */
/* A failure return code is then set and control returns to the operating */
/* system. The job this exit program is running in will then end. */
/* */
/* When this function gets called, myData-&gt;role may still contain the */
/* UnknownRole value if an exception occurred before this node's role */
/* value was set. To be completely correct, the role should be tested */
/* for UnknownRole before making any decisions based upon the value of */
/* role. */
/* */
/***************************************************************************/
static void unexpectedExceptionHandler(_INTRPT_Hndlr_Parms_T
*exData) {
/*-----------------------------------------------------------------------*/
/* */
/* Get a pointer to the structure containing data we passed to the */
/* exception handler. */
/* */
/*-----------------------------------------------------------------------*/
HandlerDataT *myData = (HandlerDataT *)exData-&gt;Com_Area;
/*-----------------------------------------------------------------------*/
/* */
/* Perform as much cleanup function as necessary. Some global state */
/* information may must be kept so the exception handler knows what */
/* steps were completed before the failure occurred and thus knows what */
/* cleanup steps must be performed. This state information could be */
/* kept in the HandlerDataT structure or it could be kept in some other */
/* location that this function can address. */
/* */
/*-----------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*/
/* */
/* If this is the primary node and the application was started, end it. */
/* The application is ended because the exit program will be called again*/
/* with the Restart action code and we want the restartCrg() function to */
/* always work the same way. In addition, ending the application may */
/* clear up the condition that caused the exception that got us here. */
/* If possible, warn users and have them stop using the application so */
/* things are done in an orderly manner. */
/* */
/*-----------------------------------------------------------------------*/
endApplication(myData-&gt;actionCode,
myData-&gt;role,
myData-&gt;priorRole,
myData-&gt;crgData,
myData-&gt;epData);
/*-----------------------------------------------------------------------*/
/* */
/* Set the exit program return code. */
/* */
/*-----------------------------------------------------------------------*/
*myData-&gt;retCode = QcstFailWithRestart;
/*-----------------------------------------------------------------------*/
/* */
/* Let the exception percolate up the call stack. */
/* */
/*-----------------------------------------------------------------------*/
return;
} /* end unexpectedExceptionHandler */
/***************************************************************************/
/* */
/* This function is called any time the job this exit program is running in*/
/* is canceled. The job could be canceled due to any of the following */
/* (the list is not intended to be all inclusive)- */
/* - an API cancels an active application CRG. The End CRG, Initiate */
/* Switchover, End Cluster Node, Remove Cluster Node or Delete Cluster */
/* API cancels the job which was submitted when the exit program was */
/* called with a Start action code. */
/* - operator cancels the job from some operating system display such as */
/* Work with Active Jobs */
/* - the subsystem this job is running in is ended */
/* - all subsystems are ended */
/* - the system is powered down */
/* - an operating system machine check occurred */
/* */
/* When this function gets called, myData-&gt;role may still contain the */
/* UnknownRole value if cancelling occurred before this node's role */
/* value was set. To be completely correct, the role should be tested */
/* for UnknownRole before making any decisions based upon the value of */
/* role. */
/* */
/***************************************************************************/
static void cancelHandler(_CNL_Hndlr_Parms_T *cnlData) {
/*-----------------------------------------------------------------------*/
/* */
/* Get a pointer to the structure containing data we passed to the */
/* cancel handler. */
/* */
/*-----------------------------------------------------------------------*/
HandlerDataT *myData = (HandlerDataT *)cnlData-&gt;Com_Area;
/*-----------------------------------------------------------------------*/
/* */
/* Perform as much cleanup function as necessary. Some global state */
/* information may must be kept so the cancel handler knows what */
/* steps were completed before the job was canceled and thus knows if */
/* the function had really completed successfully or was only partially */
/* complete and thus needs some cleanup to be done. This state */
/* information could be kept in the HandlerDataT structure or it could */
/* be kept in some other location that this function can address. */
/* */
/*-----------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*/
/* */
/* This job is being canceled. If I was running the application as a */
/* result of the Start or Restart action codes, end the application now. */
/* This job is being canceled because a Switch Over or some other */
/* Cluster Resource Services API was used which affects the primary node */
/* or someone did a cancel job with a CL command, from a system display, */
/* etc. */
/*-----------------------------------------------------------------------*/
endApplication(myData-&gt;actionCode,
myData-&gt;role,
myData-&gt;priorRole,
myData-&gt;crgData,
myData-&gt;epData);
/*-----------------------------------------------------------------------*/
/* */
/* Set the exit program return code. */
/* */
/*-----------------------------------------------------------------------*/
*myData-&gt;retCode = QcstSuccessful;
/*-----------------------------------------------------------------------*/
/* */
/* Return to the operating system for final ending of the job. */
/* */
/*-----------------------------------------------------------------------*/
return;
} /* end cancelHandler */
/***************************************************************************/
/* */
/* A common routine used to end the application by various action code */
/* functions, the exception handler, and the cancel handler. */
/* */
/***************************************************************************/
static void endApplication(unsigned int actionCode,
int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
if ( role == QcstPrimaryNodeRole
&amp;&amp;
crgData-&gt;Original_Cluster_Res_Grp_Stat == QcstCrgActive)
{
/*---------------------------------------------------------------------*/
/* */
/* Add logic to end the application here. You may need to add logic */
/* to determine if the application is still running because this */
/* function could be called once for an action code and again from */
/* the cancel handler (End CRG is an example). */
/* */
/*---------------------------------------------------------------------*/
/*---------------------------------------------------------------------*/
/* */
/* After the application has ended, update the data area to indicate */
/* the application is no longer running. */
/* */
/*---------------------------------------------------------------------*/
setApplCrgDataArea(Appl_Ended);
}
return;
} /* end endApplication */
/***************************************************************************/
/* */
/* Print out the data passed to this program. */
/* */
/***************************************************************************/
static void printParms(int actionCode,
int role,
int priorRole,
Qcst_EXTP0100_t *crgData,
EpData *epData) {
unsigned int i;
char *str;
/* Print the action code. */
printf("%s", "Action_Code = ");
printActionCode(actionCode);
/* Print the action code dependent data. */
printf("%s", " Action_Code_Dependent_Data = ");
switch (crgData-&gt;Action_Code_Dependent_Data) {
case QcstNoDependentData: str = "QcstNoDependentData";
break;
case QcstMerge: str = "QcstMerge";
break;
case QcstJoin: str = "QcstJoin";
break;
case QcstPartitionFailure: str = "QcstPartitionFailure";
break;
case QcstNodeFailure: str = "QcstNodeFailure";
break;
case QcstMemberFailure: str = "QcstMemberFailure";
break;
case QcstEndNode: str = "QcstEndNode";
break;
case QcstRemoveNode: str = "QcstRemoveNode";
break;
case QcstApplFailure: str = "QcstApplFailure";
break;
case QcstResourceEnd: str = "QcstResourceEnd";
break;
case QcstDltCluster: str = "QcstDltCluster";
break;
case QcstRmvRcvyDmnNode: str = "QcstRmvRcvyDmnNode";
break;
case QcstDltCrg: str = "QcstDltCrg";
break;
default: str = "unknown action code dependent data";
}
printf("%s \n", str);
/* Print the prior action code. */
printf("%s", " Prior_Action_Code = ");
if (crgData-&gt;Prior_Action_Code)
printActionCode(crgData-&gt;Prior_Action_Code);
printf("\n");
/* Print the cluster name. */
printStr(" Cluster_Name = ",
crgData-&gt;Cluster_Name, sizeof(Qcst_Cluster_Name_t));
/* Print the CRG name. */
printStr(" Cluster_Resource_Group_Name = ",
crgData-&gt;Cluster_Resource_Group_Name,
sizeof(Qcst_Crg_Name_t));
/* Print the CRG type. */
printf("%s \n", " Cluster_Resource_Group_Type =
QcstCrgApplResiliency");
/* Print the CRG status. */
printf("%s", " Cluster_Resource_Group_Status = ");
printCrgStatus(crgData-&gt;Cluster_Resource_Group_Status);
/* Print the CRG original status. */
printf("%s", " Original_Cluster_Res_Grp_Stat = ");
printCrgStatus(crgData-&gt;Original_Cluster_Res_Grp_Stat);
/* Print the Distribute Information queue name. */
printStr(" DI_Queue_Name = ",
crgData-&gt;DI_Queue_Name,
sizeof(crgData-&gt;DI_Queue_Name));
printStr(" DI_Queue_Library_Name = ",
crgData-&gt;DI_Queue_Library_Name,
sizeof(crgData-&gt;DI_Queue_Library_Name));
/* Print the CRG attributes. */
printf("%s", " Cluster_Resource_Group_Attr = ");
if (crgData-&gt;Cluster_Resource_Group_Attr &amp;
QcstTcpConfigByUsr)
printf("%s", "User Configures IP Takeover Address");
printf("\n");
/* Print the ID of this node. */
printStr(" This_Nodes_ID = ",
crgData-&gt;This_Nodes_ID, sizeof(Qcst_Node_Id_t));
/* Print the role of this node. */
printf("%s %d \n", " this node's role = ", role);
/* Print the prior role of this node. */
printf("%s %d \n", " this node's prior role = ", priorRole);
/* Print which recovery domain this role comes from. */
printf("%s", " Node_Role_Type = ");
if (crgData-&gt;Node_Role_Type == QcstCurrentRcvyDmn)
printf("%s \n", "QcstCurrentRcvyDmn");
else
printf("%s \n", "QcstPreferredRcvyDmn");
/* Print the ID of the changing node (if any). */
printStr(" Changing_Node_ID = ",
crgData-&gt;Changing_Node_ID, sizeof(Qcst_Node_Id_t));
/* Print the role of the changing node (if any). */
printf("%s", " Changing_Node_Role = ");
if (crgData-&gt;Changing_Node_Role == -3)
printf("%s \n", "*LIST");
else if (crgData-&gt;Changing_Node_Role == -2)
printf("%s \n", "does not apply");
else
printf("%d \n", crgData-&gt;Changing_Node_Role);
/* Print the takeover IP address. */
printStr(" Takeover_IP_Address = ",
crgData-&gt;Takeover_IP_Address,
sizeof(Qcst_TakeOver_IP_Address_t));
/* Print the job name. */
printStr(" Job_Name = ", crgData-&gt;Job_Name, 10);
/* Print the CRG changes. */
printf("%s \n", " Cluster_Resource_Group_Changes = ");
if (crgData-&gt;Cluster_Resource_Group_Changes &amp;
QcstRcvyDomainChange)
printf(" %s \n", "Recovery domain changed");
if (crgData-&gt;Cluster_Resource_Group_Changes &amp;
QcstTakeOverIpAddrChange)
printf(" %s \n", "Takeover IP address changed");
/* Print the failover wait time. */
printf("%s", "Failover_Wait_Time = ");
if (crgData-&gt;Failover_Wait_Time == QcstFailoverWaitForever)
printf("%d %s \n", crgData-&gt;Failover_Wait_Time, "Wait
forever");
else if (crgData-&gt;Failover_Wait_Time == QcstFailoverNoWait)
printf("%d %s \n", crgData-&gt;Failover_Wait_Time, "No wait");
else
printf("%d %s \n", crgData-&gt;Failover_Wait_Time, "minutes");
/* Print the failover default action. */
printf("%s", "Failover_Default_Action = ");
if (crgData-&gt;Failover_Default_Action == QcstFailoverProceed)
printf("%d %s \n", crgData-&gt;Failover_Default_Action,
"Proceed");
else
printf("%d %s \n", crgData-&gt;Failover_Default_Action,
"Cancel");
/* Print the failover message queue name. */
printStr(" Failover_Msg_Queue = ",
crgData-&gt;Failover_Msg_Queue,
sizeof(crgData-&gt;Failover_Msg_Queue));
printStr(" Failover_Msg_Queue_Lib = ",
crgData-&gt;Failover_Msg_Queue_Lib,
sizeof(crgData-&gt;Failover_Msg_Queue_Lib));
/* Print the cluster version. */
printf("%s %d \n",
" Cluster_Version = ", crgData-&gt;Cluster_Version);
/* Print the cluster version mod level */
printf("%s %d \n",
" Cluster_Version_Mod_Level = ",
crgData-&gt;Cluster_Version_Mod_Level);
/* Print the requesting user profile. */
printStr(" Req_User_Profile = ",
crgData-&gt;Req_User_Profile,
sizeof(crgData-&gt;Req_User_Profile));
/* Print the length of the data in the structure. */
printf("%s %d \n",
" Length_Info_Returned = ",
crgData-&gt;Length_Info_Returned);
/* Print the offset to the recovery domain array. */
printf("%s %d \n",
" Offset_Rcvy_Domain_Array = ",
crgData-&gt;Offset_Rcvy_Domain_Array);
/* Print the number of nodes in the recovery domain array. */
printf("%s %d \n",
" Number_Nodes_Rcvy_Domain = ",
crgData-&gt;Number_Nodes_Rcvy_Domain);
/* Print the current/new recovery domain. */
printRcvyDomain(" The recovery domain:",
crgData-&gt;Number_Nodes_Rcvy_Domain,
(Qcst_Rcvy_Domain_Array1_t *)
((char *)crgData +
crgData-&gt;Offset_Rcvy_Domain_Array));
/* Print the offset to the prior recovery domain array. */
printf("%s %d \n",
" Offset_Prior_Rcvy_Domain_Array = ",
crgData-&gt;Offset_Prior_Rcvy_Domain_Array);
/* Print the number of nodes in the prior recovery domain array. */
printf("%s %d \n",
" Number_Nodes_Prior_Rcvy_Domain = ",
crgData-&gt;Number_Nodes_Prior_Rcvy_Domain);
/* Print the prior recovery domain if one was passed. */
if (crgData-&gt;Offset_Prior_Rcvy_Domain_Array) {
printRcvyDomain(" The prior recovery domain:",
crgData-&gt;Number_Nodes_Prior_Rcvy_Domain,
(Qcst_Rcvy_Domain_Array1_t *)
((char *)crgData +
crgData-&gt;Offset_Prior_Rcvy_Domain_Array));
}
return;
} /* end printParms */
/***************************************************************************/
/* */
/* Print a string for the action code. */
/* */
/***************************************************************************/
static void printActionCode(unsigned int ac) {
char *code;
switch (ac) {
case QcstCrgAcInitialize: code = "QcstCrgAcInitialize";
break;
case QcstCrgAcStart: code = "QcstCrgAcStart";
break;
case QcstCrgAcRestart: code = "QcstCrgAcRestart";
break;
case QcstCrgAcEnd: code = "QcstCrgAcEnd";
break;
case QcstCrgAcDelete: code = "QcstCrgAcDelete";
break;
case QcstCrgAcReJoin: code = "QcstCrgAcReJoin";
break;
case QcstCrgAcFailover: code = "QcstCrgAcFailover";
break;
case QcstCrgAcSwitchover: code = "QcstCrgAcSwitchover";
break;
case QcstCrgAcAddNode: code = "QcstCrgAcAddNode";
break;
case QcstCrgAcRemoveNode: code = "QcstCrgAcRemoveNode";
break;
case QcstCrgAcChange: code = "QcstCrgAcChange";
break;
case QcstCrgAcDeleteCommand: code = "QcstCrgAcDeleteCommand";
break;
case QcstCrgAcUndo: code = "QcstCrgAcUndo";
break;
case QcstCrgEndNode: code = "QcstCrgEndNode";
break;
case QcstCrgAcAddDevEnt: code = "QcstCrgAcAddDevEnt";
break;
case QcstCrgAcRmvDevEnt: code = "QcstCrgAcRmvDevEnt";
break;
case QcstCrgAcChgDevEnt: code = "QcstCrgAcChgDevEnt";
break;
case QcstCrgAcChgNodeStatus: code = "QcstCrgAcChgNodeStatus";
break;
case QcstCrgAcCancelFailover: code = "QcstCrgAcCancelFailover";
break;
case QcstCrgAcVerificationPhase: code =
"QcstCrgAcVerificationPhase";
break;
default: code = "unknown action code";
break;
}
printf("%s", code);
return;
} /* end printActionCode */
/***************************************************************************/
/* */
/* Print the CRG status. */
/* */
/***************************************************************************/
static void printCrgStatus(int status) {
char * str;
switch (status) {
case QcstCrgActive: str = "QcstCrgActive";
break;
case QcstCrgInactive: str= "QcstCrgInactive";
break;
case QcstCrgIndoubt: str = "QcstCrgIndoubt";
break;
case QcstCrgRestored: str = "QcstCrgRestored";
break;
case QcstCrgAddnodePending: str =
"QcstCrgAddnodePending";
break;
case QcstCrgDeletePending: str = "QcstCrgDeletePending";
break;
case QcstCrgChangePending: str = "QcstCrgChangePending";
break;
case QcstCrgEndCrgPending: str = "QcstCrgEndCrgPending";
break;
case QcstCrgInitializePending: str =
"QcstCrgInitializePending";
break;
case QcstCrgRemovenodePending: str =
"QcstCrgRemovenodePending";
break;
case QcstCrgStartCrgPending: str =
"QcstCrgStartCrgPending";
break;
case QcstCrgSwitchOverPending: str =
"QcstCrgSwitchOverPending";
break;
case QcstCrgDeleteCmdPending: str =
"QcstCrgDeleteCmdPending";
break;
case QcstCrgAddDevEntPending: str =
"QcstCrgAddDevEntPending";
break;
case QcstCrgRmvDevEntPending: str =
"QcstCrgRmvDevEntPending";
break;
case QcstCrgChgDevEntPending: str =
"QcstCrgChgDevEntPending";
break;
case QcstCrgChgNodeStatusPending: str =
"QcstCrgChgNodeStatusPending";
break;
default: str = "unknown CRG status";
}
printf("%s \n", str);
return;
} /* end printCrgStatus */
/***************************************************************************/
/* */
/* Print the recovery domain. */
/* */
/***************************************************************************/
static void printRcvyDomain(char *str,
unsigned int count,
Qcst_Rcvy_Domain_Array1_t *rd) {
unsigned int i;
printf("\n %s \n", str);
for (i=1; i&amp;lt;=count; i++) {
printStr(" Node_ID = ", rd-&gt;Node_ID,
sizeof(Qcst_Node_Id_t));
printf("%s %d \n", " Node_Role = ", rd-&gt;Node_Role);
printf("%s", " Membership_Status = ");
switch (rd-&gt;Membership_Status) {
case 0: str = "Active";
break;
case 1: str = "Inactive";
break;
case 2: str = "Partition";
break;
default: str = "unknown node status";
}
printf("%s \n", str);
rd++;
}
return;
} /* end printRcvyDomain */
/***************************************************************************/
/* */
/* Concatenate a null terminated string and a non null terminated string */
/* and print it. */
/* */
/***************************************************************************/
static void printStr(char *s1, char *s2, unsigned int len) {
char buffer[132];
memset(buffer, 0x00, sizeof(buffer));
memcpy(buffer, s1, strlen(s1));
strncat(buffer, s2, len);
printf("%s \n", buffer);
return;
} /* end printStr */
</pre>
</div>
<div>
<div class="familylinks">
<div class="parentlink"><strong>Parent topic:</strong> <a href="rzaigapplicationscrg.htm" title="An application cluster resource group manages application resiliency.">Application CRG considerations</a></div>
</div>
</div>
</body>
</html>