dbPath = $dbPath; $this->clusterName = $clusterName; $this->logger = new HMCLogger("HMC"); $this->dbHandle = new HMCDBAccessor($dbPath); if (!isset($GLOBALS["PHP_EXEC_PATH"])) { $GLOBALS["PHP_EXEC_PATH"] = "/usr/bin/php"; } // TODO if (!isset($GLOBALS["CLUSTERMAIN_PATH"])) { $GLOBALS["CLUSTERMAIN_PATH"] = "ClusterMain.php"; } $this->command = $GLOBALS["PHP_EXEC_PATH"] . " " . $GLOBALS["CLUSTERMAIN_PATH"]; } /** * Function to deploy and start HDP across the whole cluster * Runs in the background * @return mixed * txn_id: transaction id to refer to to get progress updates and logs * array ( "txn_id" => $txn_id ); */ public function deployHDP() { $action = "deployHDP"; $msg = "Deploying cluster"; $args = " -c " . $this->clusterName . " -d " . $this->dbPath . " -a deploy "; return $this->internalTrigger($action, $msg, $args); } /** * Function to deploy all the required rpms and start all required * services on a given node * Runs in the background * @param array $nodes Hostnames of the nodes to be deployed * @return mixed * txn_id: transaction id to refer to to get progress updates and logs * array ( "txn_id" => $txn_id ); */ public function deployNodes($nodes) { $action = "deployNodes"; $nodeList = implode(",", $nodes); $msg = "Deploying on nodes, list=" . $nodeList; $args = " -c " . $this->clusterName . " -d " . $this->dbPath . " -a deployNode "; foreach ($nodes as $node) { $args .= " -n " . $node; } return $this->internalTrigger($action, $msg, $args); } /** * Function to start all the services in order. * Runs in the background. * @return mixed * txn_id: transaction id to refer to to get progress updates and logs * array ( "txn_id" => $txn_id ); */ public function startAllServices() { $action = "startAllServices"; $msg = "Starting all services"; $args = " -c " . $this->clusterName . " -d " . $this->dbPath . " -a startAll "; return $this->internalTrigger($action, $msg, $args); } /** * Function to stop all the services in order. * Runs in the background. * @return mixed * txn_id: transaction id to refer to to get progress updates and logs * array ( "txn_id" => $txn_id ); */ public function stopAllServices() { $action = "stopAllServices"; $msg = "Stopping all services"; $args = " -c " . $this->clusterName . " -d " . $this->dbPath . " -a stopAll "; return $this->internalTrigger($action, $msg, $args); } /** * NOT SUPPORTED * Function to start given services. * Runs in the background. * @return mixed * txn_id: transaction id to refer to to get progress updates and logs * array ( "txn_id" => $txn_id ); */ public function startServices($services) { $action = "startServices"; $svcList = implode(",", $services); $msg = "Starting services, list=".$svcList; $args = " -c " . $this->clusterName . " -d " . $this->dbPath . " -a start "; foreach ($services as $svc) { $args .= " -s " . $svc; } return $this->internalTrigger($action, $msg, $args); } /** * NOT SUPPORTED * Function to stop given services. * Runs in the background. * @return mixed * txn_id: transaction id to refer to to get progress updates and logs * array ( "txn_id" => $txn_id ); */ public function stopServices($services) { $action = "stopServices"; $svcList = implode(",", $services); $msg = "Stopping services, list=".$svcList; $args = " -c " . $this->clusterName . " -d " . $this->dbPath . " -a stop "; foreach ($services as $svc) { $args .= " -s " . $svc; } return $this->internalTrigger($action, $msg, $args); } /** * NOT SUPPORTED * Function to start a given service and all the services it depends upon to * start successfully in the required order. * Runs in the background. * @return mixed * txn_id: transaction id to refer to to get progress updates and logs * array ( "txn_id" => $txn_id ); */ public function startDependentServices($service) { // generate a txn id which is returned back to the UI for progress // monitoring // use popen/pclose to start a background process // background script: // generate site.pp to start service and dependent services in required order // update progress, make logs accessible error_log("Start dependent services is not supported"); exit (1); } /** * NOT SUPPORTED * Function to stop a given service and all the services that depend upon it * in the required order. * Runs in the background. * @return mixed * txn_id: transaction id to refer to to get progress updates and logs * array ( "txn_id" => $txn_id ); */ public function stopDependentServices($service) { // generate a txn id which is returned back to the UI for progress // monitoring // use popen/pclose to start a background process // background script: // generate site.pp to stop service and dependent services in required order // update progress, make logs accessible error_log("Stop dependent services is not supported"); exit (1); } /** * NOT SUPPORTED * Function to reconfigure a cluster by first stopping the whole cluster, * re-pushing new configs to all nodes and restarting all services. * Runs in the background. * @return mixed * txn_id: transaction id to refer to to get progress updates and logs * array ( "txn_id" => $txn_id ); */ public function reconfigureHDP() { // generate a txn id which is returned back to the UI for progress // monitoring // use popen/pclose to start a background process // background script: // generate site.pp to stop all services in required order // update progress, make logs accessible // generate site.pp with new configs // update progress, make logs accessible // generate site.pp to start all services in required order // update progress, make logs accessible error_log("Re-configuring is not supported"); exit (1); } /** * Function to reconfigure services by first stopping the services and the * required dependencies, re-pushing new configs to required nodes and * restarting all the required services. * Runs in the background. * @param array services to re-configure * @return mixed * txn_id: transaction id to refer to to get progress updates and logs * array ( "txn_id" => $txn_id ); */ public function reconfigureServices($services) { $action = "reconfigureServices"; $svcList = implode(",", $services); $msg = "Reconfiguring services, list=".$svcList; $args = " -c " . $this->clusterName . " -d " . $this->dbPath . " -a reconfigure "; foreach ($services as $svc) { $args .= " -s " . $svc; } return $this->internalTrigger($action, $msg, $args); } /** * Get progress update for a given transaction. * @param txnId Transaction Id * @return mixed * array ( "txn_id" => $txn_id, * "result" => 0, * "processRunning" => bool, * "subTxns" => array ( * array ( * "subTxnId" => * "parentSubTxnId" => * "state" => * "description" => * ) * ) * ) */ public function getProgress($txnId) { // given the txn id generated for one of the supported actions, provide // the current progress update // this could also be a trigger to update the current state if needed $response = array ( "result" => 0, "error" => ""); $txnInfo = $this->dbHandle->getTransactionStatusInfo($this->clusterName, $txnId); if ($txnInfo === FALSE || $txnInfo["result"] != 0) { return $txnInfo; } $pidInfo = json_decode($txnInfo["pidInfo"], true); $procRunning = HMCTxnUtils::checkTxnProcessStatus($pidInfo); $response["processRunning"] = $procRunning; $subTxnInfo = $this->dbHandle->getAllSubTransactionsInfo($this->clusterName, $txnId); if ($subTxnInfo === FALSE || $subTxnInfo["result"] != 0) { return $subTxnInfo; } $orderedSubTxns = $this->orderSubTxns($subTxnInfo["subTxns"]); $response["txnId"] = $txnId; $response["subTxns"] = array(); foreach($orderedSubTxns as $sTxnId => $sTxn) { if (isset($sTxn["opStatus"])) { $sTxn["opStatus"] = json_decode($sTxn["opStatus"], true); } else { $sTxn["opStatus"] = array(); } array_push($response["subTxns"], $sTxn); } return $response; } public function orderSubTxns($subTxns) { $subTxnCount = count($subTxns); $parentIndexedSubTxns = array(); $allSubTxns = array(); foreach ($subTxns as $subTxn) { $allSubTxns[$subTxn["subTxnId"]] = $subTxn; } foreach ($subTxns as $subTxn) { if (!isset($parentIndexedSubTxns[$subTxn["parentSubTxnId"]])) { $parentIndexedSubTxns[$subTxn["parentSubTxnId"]] = array(); } $parentIndexedSubTxns[$subTxn["parentSubTxnId"]][$subTxn["subTxnId"]] = $subTxn; } $rankedSubTxns = array(); $currentRank = 0; $rankedIndexes = array(); $this->_orderSubTxns($rankedSubTxns, $rankedIndexes, $currentRank, $allSubTxns, $parentIndexedSubTxns, $subTxns); assert(count($rankedSubTxns) == $subTxnCount); return $rankedSubTxns; } private function _orderSubTxns(&$rankedSubTxns, &$rankedIndexes, &$currentRank, &$allSubTxns, $parentIndexedSubTxns, $subTxnsToOrder) { $maxIndex = 0; $indexedSubTxns = array(); foreach ($subTxnsToOrder as $subTxn) { if ($subTxn["subTxnId"] > $maxIndex) { $maxIndex = $subTxn["subTxnId"]; } $indexedSubTxns[$subTxn["subTxnId"]] = $subTxn; } for ($i = 0; $i <= $maxIndex; ++$i) { if (!isset($indexedSubTxns[$i])) { continue; } $subTxn = $indexedSubTxns[$i]; $parentSubTxnId = $subTxn["parentSubTxnId"]; if (isset($parentIndexedSubTxns[$i]) && is_array($parentIndexedSubTxns[$i]) && count($parentIndexedSubTxns[$i]) > 0) { ksort($parentIndexedSubTxns[$i]); foreach ($parentIndexedSubTxns[$i] as $index => $pSubTxn) { $pSubTxnId = $pSubTxn["subTxnId"]; $ppSubTxnId = $pSubTxn["parentSubTxnId"]; if (!isset($rankedIndexes[$pSubTxnId])) { $this->_orderSubTxns($rankedSubTxns, $rankedIndexes, $currentRank, $allSubTxns, $parentIndexedSubTxns, $parentIndexedSubTxns[$ppSubTxnId]); } if (!isset($rankedIndexes[$pSubTxnId])) { $pSubTxn["rank"] = $currentRank; $rankedSubTxns[$currentRank] = $pSubTxn; $rankedIndexes[$pSubTxn["subTxnId"]] = TRUE; ++$currentRank; } } } if (!isset($rankedIndexes[$i])) { $subTxn["rank"] = $currentRank; $rankedSubTxns[$currentRank] = $subTxn; $rankedIndexes[$i] = TRUE; ++$currentRank; } } return $rankedSubTxns; } /** * Get logs for a given transaction * @param string $txnId Transaction Id */ public function getLogs($txnId) { // get the logs for a given transaction triggered earlier. // not sure if we want to support filtering by log level for now $response = array ( "result" => 0, "error" => ""); $txnInfo = $this->dbHandle->getTransactionStatusInfo($this->clusterName, $txnId); if ($txnInfo === FALSE || $txnInfo["result"] != 0) { return $txnInfo; } $subTxnInfo = $this->dbHandle->getAllSubTransactionsInfo($this->clusterName, $txnId); if ($subTxnInfo === FALSE || $subTxnInfo["result"] != 0) { return $subTxnInfo; } $puppetInvoker = new PuppetInvoker($this->dbPath); $response["txnId"] = $txnId; $response["subTxns"] = array(); foreach ($subTxnInfo["subTxns"] as $subTxnId => $subTxn) { $nodes = array(); $response["subTxns"][$subTxn["subTxnId"]] = array( "nodeReport" => array(), "nodeLogs" => array()); if (!isset($subTxn["opStatus"])) { continue; } $opStatus = json_decode($subTxn["opStatus"], true); if (!isset($opStatus["nodeReport"])) { continue; } $nodeReport = $opStatus["nodeReport"]; $response["subTxns"][$subTxn["subTxnId"]]["nodeReport"] = $nodeReport; $keys = array ( "PUPPET_KICK_FAILED", "PUPPET_OPERATION_FAILED", "PUPPET_OPERATION_SUCCEEDED" ); foreach ($keys as $key) { if (isset($nodeReport[$key]) && is_array($nodeReport[$key])) { $nodes = array_merge($nodes, $nodeReport[$key]); } } $transaction = new Transaction($txnId, $subTxn["subTxnId"], $subTxn["parentSubTxnId"]); $puppetLogs = $puppetInvoker->getReports($nodes, $transaction); $this->logger->log_debug("Got puppet reports for transaction=" .$transaction->toString() . ", nodes=" . print_r($nodes, true) . ", logs=" . print_r($puppetLogs, true)); $response["subTxns"][$subTxn["subTxnId"]]["nodeLogs"] = $puppetLogs; } return $response; } private function internalTrigger($action, $msg, $args) { $this->logger->log_info("HMC triggering action=" . $action . ", " . $msg); $response = array ( "result" => 0, "error" => ""); $statusInfo = array ("function" => "HMC::$action", "action" => $msg); $txnId = HMCTxnUtils::createNewTransaction($this->dbHandle, $this->clusterName, $statusInfo); if ($txnId === FALSE) { $error = "Failed to create a new transaction, action=" . $action; $this->logger->log_error($error); return array ("result" => 1, "error" => $error); } $response["txnId"] = $txnId; $args .= " -x " . $txnId; $this->logger->log_debug("Triggering background process" . ", clusterName=" . $this->clusterName . ", txnId=" . $txnId . ", command=" . $this->command . ", args=" . $args); $backgroundPid = HMCTxnUtils::execBackgroundProcess($this->dbHandle, $this->clusterName, $txnId, $this->command, $args, ""); if ($backgroundPid === FALSE) { $error = "Failed to trigger background process, action=" . $action; $this->logger->log_error($error); $response["result"] = 1; $response["error"] = $error; } return $response; } public function uninstallHDP($wipeoutData = FALSE) { $this->logger->log_info("Triggering uninstall" . ", clusterName=" . $this->clusterName ); $action = "uninstallHDP"; $msg = "Uninstalling cluster, wipeout=" . $wipeoutData; $args = " -c " . $this->clusterName . " -d " . $this->dbPath; if (!$wipeoutData) { $args .= " -a uninstallAll "; } else { $args .= " -a wipeout "; } return $this->internalTrigger($action, $msg, $args); } } ?>