This code example contains code for a sample application cluster resource group exit program.
You can find this code example in the QUSRTOOL library.
By using the code examples, you agree to the terms of the Code license and disclaimer information.
/***************************************************************************/ /* */ /* 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->Offset_Rcvy_Domain_Array, crgData->Number_Nodes_Rcvy_Domain); if (crgData->Offset_Prior_Rcvy_Domain_Array) hdlData.priorRole = getMyRole(crgData, crgData->Offset_Prior_Rcvy_Domain_Array, crgData->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->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->This_Nodes_ID, nodeData->Node_ID, sizeof(Qcst_Node_Id_t)) && iter < count ) { nodeData++; iter++; } if (iter < count) return nodeData->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 <= 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->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->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->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->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 && 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(&crgData->This_Nodes_ID, &crgData->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(&crgData->DI_Queue_Name, Nulls, sizeof(crgData->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(&crgData->This_Nodes_ID, &crgData->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->Prior_Action_Code <= MaxAc ) return (*undoFcn[crgData-<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( &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(&data, sizeof(data), dataAreaName, offsetof(Qcst_HAAPPO_t,Data_Status)+1, /* API wants a 1 origin */ sizeof(data.ready), &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 >= maxWaitTime) return QcstFailWithOutRestart; /*---------------------------------------------------------------------*/ /* */ /* Wait to allow the data CRGs to become ready. */ /* */ /*---------------------------------------------------------------------*/ waittime(&timeToWait, options); timeWaited += WaitSecondsIncrement; /*---------------------------------------------------------------------*/ /* */ /* Get information from the data area again to see if the data CRGs are*/ /* ready. */ /* */ /*---------------------------------------------------------------------*/ QWCRDTAA(&data, sizeof(data), dataAreaName, offsetof(Qcst_HAAPPO_t,Data_Status)+1, /* API wants a 1 origin */ sizeof(data.ready), &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->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->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->actionCode, myData->role, myData->priorRole, myData->crgData, myData->epData); /*-----------------------------------------------------------------------*/ /* */ /* Set the exit program return code. */ /* */ /*-----------------------------------------------------------------------*/ *myData->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->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->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->actionCode, myData->role, myData->priorRole, myData->crgData, myData->epData); /*-----------------------------------------------------------------------*/ /* */ /* Set the exit program return code. */ /* */ /*-----------------------------------------------------------------------*/ *myData->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 && crgData->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->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->Prior_Action_Code) printActionCode(crgData->Prior_Action_Code); printf("\n"); /* Print the cluster name. */ printStr(" Cluster_Name = ", crgData->Cluster_Name, sizeof(Qcst_Cluster_Name_t)); /* Print the CRG name. */ printStr(" Cluster_Resource_Group_Name = ", crgData->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->Cluster_Resource_Group_Status); /* Print the CRG original status. */ printf("%s", " Original_Cluster_Res_Grp_Stat = "); printCrgStatus(crgData->Original_Cluster_Res_Grp_Stat); /* Print the Distribute Information queue name. */ printStr(" DI_Queue_Name = ", crgData->DI_Queue_Name, sizeof(crgData->DI_Queue_Name)); printStr(" DI_Queue_Library_Name = ", crgData->DI_Queue_Library_Name, sizeof(crgData->DI_Queue_Library_Name)); /* Print the CRG attributes. */ printf("%s", " Cluster_Resource_Group_Attr = "); if (crgData->Cluster_Resource_Group_Attr & QcstTcpConfigByUsr) printf("%s", "User Configures IP Takeover Address"); printf("\n"); /* Print the ID of this node. */ printStr(" This_Nodes_ID = ", crgData->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->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->Changing_Node_ID, sizeof(Qcst_Node_Id_t)); /* Print the role of the changing node (if any). */ printf("%s", " Changing_Node_Role = "); if (crgData->Changing_Node_Role == -3) printf("%s \n", "*LIST"); else if (crgData->Changing_Node_Role == -2) printf("%s \n", "does not apply"); else printf("%d \n", crgData->Changing_Node_Role); /* Print the takeover IP address. */ printStr(" Takeover_IP_Address = ", crgData->Takeover_IP_Address, sizeof(Qcst_TakeOver_IP_Address_t)); /* Print the job name. */ printStr(" Job_Name = ", crgData->Job_Name, 10); /* Print the CRG changes. */ printf("%s \n", " Cluster_Resource_Group_Changes = "); if (crgData->Cluster_Resource_Group_Changes & QcstRcvyDomainChange) printf(" %s \n", "Recovery domain changed"); if (crgData->Cluster_Resource_Group_Changes & QcstTakeOverIpAddrChange) printf(" %s \n", "Takeover IP address changed"); /* Print the failover wait time. */ printf("%s", "Failover_Wait_Time = "); if (crgData->Failover_Wait_Time == QcstFailoverWaitForever) printf("%d %s \n", crgData->Failover_Wait_Time, "Wait forever"); else if (crgData->Failover_Wait_Time == QcstFailoverNoWait) printf("%d %s \n", crgData->Failover_Wait_Time, "No wait"); else printf("%d %s \n", crgData->Failover_Wait_Time, "minutes"); /* Print the failover default action. */ printf("%s", "Failover_Default_Action = "); if (crgData->Failover_Default_Action == QcstFailoverProceed) printf("%d %s \n", crgData->Failover_Default_Action, "Proceed"); else printf("%d %s \n", crgData->Failover_Default_Action, "Cancel"); /* Print the failover message queue name. */ printStr(" Failover_Msg_Queue = ", crgData->Failover_Msg_Queue, sizeof(crgData->Failover_Msg_Queue)); printStr(" Failover_Msg_Queue_Lib = ", crgData->Failover_Msg_Queue_Lib, sizeof(crgData->Failover_Msg_Queue_Lib)); /* Print the cluster version. */ printf("%s %d \n", " Cluster_Version = ", crgData->Cluster_Version); /* Print the cluster version mod level */ printf("%s %d \n", " Cluster_Version_Mod_Level = ", crgData->Cluster_Version_Mod_Level); /* Print the requesting user profile. */ printStr(" Req_User_Profile = ", crgData->Req_User_Profile, sizeof(crgData->Req_User_Profile)); /* Print the length of the data in the structure. */ printf("%s %d \n", " Length_Info_Returned = ", crgData->Length_Info_Returned); /* Print the offset to the recovery domain array. */ printf("%s %d \n", " Offset_Rcvy_Domain_Array = ", crgData->Offset_Rcvy_Domain_Array); /* Print the number of nodes in the recovery domain array. */ printf("%s %d \n", " Number_Nodes_Rcvy_Domain = ", crgData->Number_Nodes_Rcvy_Domain); /* Print the current/new recovery domain. */ printRcvyDomain(" The recovery domain:", crgData->Number_Nodes_Rcvy_Domain, (Qcst_Rcvy_Domain_Array1_t *) ((char *)crgData + crgData->Offset_Rcvy_Domain_Array)); /* Print the offset to the prior recovery domain array. */ printf("%s %d \n", " Offset_Prior_Rcvy_Domain_Array = ", crgData->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->Number_Nodes_Prior_Rcvy_Domain); /* Print the prior recovery domain if one was passed. */ if (crgData->Offset_Prior_Rcvy_Domain_Array) { printRcvyDomain(" The prior recovery domain:", crgData->Number_Nodes_Prior_Rcvy_Domain, (Qcst_Rcvy_Domain_Array1_t *) ((char *)crgData + crgData->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<=count; i++) { printStr(" Node_ID = ", rd->Node_ID, sizeof(Qcst_Node_Id_t)); printf("%s %d \n", " Node_Role = ", rd->Node_Role); printf("%s", " Membership_Status = "); switch (rd->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 */