2624 lines
140 KiB
HTML
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->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 &lt; count
|
||
|
) {
|
||
|
nodeData++;
|
||
|
iter++;
|
||
|
}
|
||
|
|
||
|
if (iter &lt; 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 &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->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 &lt;= MaxAc )
|
||
|
return (*undoFcn[crgData-&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( &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&lt;=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 */
|
||
|
|
||
|
</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>
|