array ( "deployPostProcess" ), "HMC::deployNodes" => array ( "restoreDeployedStatePostProcess" ), "HMC::uninstallHDP" => array ( "deBootStrap" ), "HMC::startAllServices" => array ( "restoreDeployedStatePostProcess" ), "HMC::stopAllServices" => array ( "restoreDeployedStatePostProcess" ), "HMC::startServices" => array ( "restoreDeployedStatePostProcess" ), "HMC::stopServices" => array ( "restoreDeployedStatePostProcess" ), "HMC::reconfigureServices" => array ( "restoreDeployedStatePostProcess" ) ); $dbAccessor = new HMCDBAccessor($GLOBALS["DB_PATH"]); function fetchTxnProgress( $txnId ) { global $dbPath; global $clusterName; $hmc = new HMC($dbPath, $clusterName); $progress = $hmc->getProgress($txnId); return $progress; } function sortProgressStatesByRank( $first, $second ) { if( $first['rank'] == $second['rank'] ) { return 0; } return ($first['rank'] < $second['rank']) ? -1 : 1; } $progress = fetchTxnProgress($txnId); // TODO XXX Check for $progress['result'] and $progress['error'] here, before proceeding. /* Tack on some additional state to make life on the frontend easier. */ $progress['encounteredError'] = false; /* Marker to keep track of whether at least one subTxn has been kicked off. */ $atLeastOneSubTxnInProgress = false; /* Sort the subTxns array inside $progress by rank, and then remove all notion * of rank from the sorted array we're going to return. */ usort( $progress['subTxns'], 'sortProgressStatesByRank' ); foreach( $progress['subTxns'] as &$progressSubTxn ) { unset( $progressSubTxn['rank'] ); /* Any one subTxn failing means we want the frontend to bail. */ if( $progressSubTxn['progress'] == 'FAILED' ) { $progress['encounteredError'] = true; } /* We need to make sure at least one subTxn is not pending before * sending a progress report back to the frontend - if not, the * progress states aren't yet finalized and will change across * invocations to this webservice, so we prefer to wait before * showing anything. */ if( $progressSubTxn['progress'] != 'PENDING' ) { $atLeastOneSubTxnInProgress = true; } } $lastTxnIndex = -1; if ($atLeastOneSubTxnInProgress) { $lastTxnIndex = count($progress['subTxns']) - 1; } /* If at least one subTxn isn't in progress, signal to the frontend that * there's nothing worthy for it to process yet. */ if (!$atLeastOneSubTxnInProgress) { $progress['subTxns'] = null; } LockAcquire(HMC_CLUSTER_STATE_LOCK_FILE_SUFFIX); $doPostProcess = TRUE; $clusterStateResponse = $dbAccessor->getClusterState($clusterName); $logger->log_debug("Got cluster state: ".json_encode($clusterStateResponse)); if ($clusterStateResponse['result'] != 0) { print json_encode($clusterStateResponse); LockRelease(HMC_CLUSTER_STATE_LOCK_FILE_SUFFIX); return; } // if state is not set, should not proceed to post process. // setting post process to false allows returning appropriate txn data // without post processing. $clusterState = null; if (!isset($clusterStateResponse['state'])) { $doPostProcess = FALSE; // create an empty cluster state $clusterState = array(); } else { $clusterState = json_decode($clusterStateResponse['state'], true); $logger->log_debug("Current cluster state, " . print_r($clusterState, true)); } /* check for matching txn id. if present check if it is same * if not return with all data for txn id requested for and * do nothing for post process * setting post process to false allows returning appropriate txn data * without post processing. */ if (array_key_exists('context', $clusterState)) { $clusterContext = $clusterState['context']; if (!array_key_exists('txnId', $clusterContext) || !isset($clusterContext['txnId']) || ($clusterContext["txnId"] != $txnId)) { $logger->log_debug("TxnId does not exist ". array_key_exists("txnId", $clusterContext) . " or not set " .!isset($clusterContext["txnId"]) ." or does not match " . ($clusterContext["txnId"] != $txnId)); $doPostProcess = FALSE; } } if ($progress['processRunning'] == FALSE) { $logger->log_trace("Checking cluster state for post process state"); $context = $clusterState['context']; if (isset($context['isInPostProcess'])) { $doPostProcess = FALSE; $logger->log_trace("Post process already done before in another call"); if ($context['isInPostProcess'] == TRUE) { $logger->log_trace("Post process still in progress in another call"); $progress['processRunning'] = TRUE; } else { $logger->log_trace("Post process completed in another call"); $progress['processRunning'] = FALSE; if (!isset($context['postProcessSuccessful']) || $context['postProcessSuccessful'] == FALSE) { $progress['encounteredError'] = TRUE; if ($lastTxnIndex >= 0) { $progress['subTxns'][$lastTxnIndex]["progress"] = "FAILED"; } } } } } if ((($progress['processRunning'] == FALSE) || ($progress['encounteredError'] == TRUE)) && $doPostProcess) { // get the transaction status info from db $retval = $dbAccessor->getTransactionStatusInfo($clusterName, $txnId); if ($retval["result"] != 0) { $progress['encounteredError'] = TRUE; if ($lastTxnIndex >= 0) { $progress['subTxns'][$lastTxnIndex]["progress"] = "FAILED"; } } else { if (isset($retval['statusInfo'])) { $statusInfo = json_decode($retval['statusInfo'], true); $logger->log_debug("Status info function ".$statusInfo['function']); $logger->log_debug("Running post process functions"); // run the next script from the map // supports multiple post process functions foreach ($map[$statusInfo['function']] as $postProcessFunc) { $logger->log_debug("Post process function is ".$postProcessFunc); // setting cluster state to denote in post process $clusterState['context']['isInPostProcess'] = TRUE; $logger->log_trace("Starting post process function"); updateClusterState($clusterName, $clusterState['state'], $clusterState['displayName'], $clusterState['context']); LockRelease(HMC_CLUSTER_STATE_LOCK_FILE_SUFFIX); $retval = $postProcessFunc($clusterName, $deployUser, $txnId, $progress); LockAcquire(HMC_CLUSTER_STATE_LOCK_FILE_SUFFIX); // setting cluster state to denote post process completed $logger->log_trace("Finished post process function"); $clusterStateResponse = $dbAccessor->getClusterState($clusterName); if ($clusterStateResponse['result'] != 0) { print json_encode($clusterStateResponse); LockRelease(HMC_CLUSTER_STATE_LOCK_FILE_SUFFIX); return; } $clusterState = json_decode($clusterStateResponse['state'], true); $clusterState['context']['isInPostProcess'] = FALSE; $clusterState['context']['postProcessSuccessful'] = ($retval["result"] == 0); updateClusterState($clusterName, $clusterState['state'], $clusterState['displayName'], $clusterState['context']); $clusterStateResponse = $dbAccessor->getClusterState($clusterName); $logger->log_trace("STATE AFTER UPDATE: ".json_encode($clusterStateResponse)); if ($retval["result"] != 0) { $progress['encounteredError'] = TRUE; if ($lastTxnIndex >= 0) { $progress['subTxns'][$lastTxnIndex]["progress"] = "FAILED"; } // if the post process failed stop from calling // further post process functions. break; } } } } } LockRelease(HMC_CLUSTER_STATE_LOCK_FILE_SUFFIX); /* Clean up some more remnants that we don't need on the frontend. */ unset( $progress['result'] ); unset( $progress['error'] ); /* Create the output data... */ $jsonOutput = array( 'clusterName' => $clusterName, 'txnId' => $txnId, 'progress' => $progress ); if ($deployUser != null) { $jsonOutput['deployUser'] = $deployUser; } /* ...and spit it out. */ header("Content-type: application/json"); print (json_encode($jsonOutput)); ?>