blob: 523d833a18074a621c77ed2c80c70825f06df074 [file] [log] [blame]
<?php
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class SuggestProperties {
private $logger;
/**
* Needs a default constructor else warnings will appear.
*/
function __construct() {
$this->logger = new HMCLogger("SuggestProperties");
}
/**
* Allocate Heap Size for a component given what all processes are running
* on the host.
*/
function allocateHeapSizeForDaemon($componentName, $hostRoles, $hostInfoMap,
$allHostsToComponents, $is32bit) {
// TODO fix
// code should handle 32-bit checks - cannot assign over 4 GB for a role
// which uses 32-bit procs
// if os is 32-bit we have even more restrictions
// for now assuming 64-bit os
$this->logger->log_info("Calculating Heap Size For ".$componentName);
$host = $this->getHostForComponent($hostRoles, $componentName);
$this->logger->log_info("Model HostName for ".$componentName." ".$host);
$hostMem = $hostInfoMap[$host]["totalMem"]*0.9;
$numProcs = sizeof($allHostsToComponents["hosts"][$host]["components"]);
$normalizedMem = (int) (ceil ($hostMem/$numProcs));
if ($is32bit) {
$normalizedMem = min($normalizedMem, (pow(2,32)/(1024*1024))-1);
}
$normalizedMem = ((int)($normalizedMem/8))*8;
$this->logger->log_info("Component=" . $componentName . " Host="
. $host." Mem=".$hostMem." numComponents=".$numProcs.
" perComponentMem=".$normalizedMem);
if ($normalizedMem < 256) {
$this->logger->log_info("Normalizing memory to 256 as min required");
$normalizedMem = 256;
}
return $normalizedMem;
}
function allocateHeapSizeWithMax($componentName, $hostRoles, $hostInfoMap,
$allHostsToComponents, $is32bit, $max) {
$heapSizeT = $this->allocateHeapSizeForDaemon($componentName, $hostRoles, $hostInfoMap,
$allHostsToComponents, $is32bit);
if ($heapSizeT > $max) {
$heapSizeT = $max;
}
$this->logger->log_info("Calculating Maxed Heap Size For ".$componentName ." $heapSizeT with max $max" );
return $heapSizeT;
}
function getMaxHeapSizeForDaemon($componentName, $hostRoles, $hostInfoMap,
$allHostsToComponents, $is32bit) {
$this->logger->log_info("Calculating Max Heap Size For ".$componentName);
$host = $this->getHostForComponent($hostRoles, $componentName);
$this->logger->log_info("Model HostName for ".$componentName." ".$host);
$hostMem = $hostInfoMap[$host]["totalMem"];
$normalizedMem = $hostMem;
if ($is32bit) {
$normalizedMem = min($normalizedMem, (pow(2,32)/(1024*1024))-1);
}
$this->logger->log_info("Component=" . $componentName . " Host="
. $host." HostMem=".$hostMem
." MaxNormalizedMem=".$normalizedMem);
return $normalizedMem;
}
/**
* get the host info in a list, convert it to a map so that easy
* to lookup
*/
function createHostToInfoMap($hostInfo) {
$hosts = $hostInfo["hosts"];
$result = array();
foreach($hosts as $host) {
$result[$host["hostName"]] = $host;
}
return $result;
}
/**
* Return a single host that maps to a master service.
*/
function getHostForComponent($hostRoles, $role) {
$listHosts = $hostRoles["components"][$role]["hosts"];
foreach ($listHosts as $hostName=>$hostInfo) {
$retHost = $hostName;
break;
}
return $retHost;
}
/** Return only the enabled services.
*/
function filterEnabledServices($services) {
$enabledServices = array();
foreach($services as $serviceName=>$serviceInfo) {
if ($serviceInfo["isEnabled"] == 1) {
$enabledServices[$serviceName] = $serviceInfo;
}
}
return $enabledServices;
}
/**
* Function to suggest Properties to the user.
* It will read the db to get the sevices that are configured
* return back the configs with suggestions based on the services that are
* configured.
* NOTE: It will only return recommended configs - does not return other
* props or defaults from DB
* @param clustername the name of the cluster we are deploying/managing
* @param db database from where to read, usually pass in new HMCDBAccessor("mydb.data");
* @param updateDB bool whether to update db with suggested settings
* @return mixed
* array (
* "result" => 0,
* "error" => "",
* "configs" => array(
* "key" => "val",
* ...
* )
* );
*/
public function suggestProperties($clusterName, $db,
$updateDB) {
$result = array();
$result["result"] = 0;
$result["error"] = "";
$servicesDBInfo = $db->getAllServicesInfo($clusterName);
if ($servicesDBInfo["result"] != 0) {
$result["result"] = $servicesDBInfo["result"];
$result["error"] = $servicesDBInfo["error"];
return $result;
}
$services_tmp = $servicesDBInfo["services"];
$services = $this->filterEnabledServices($services_tmp);
$this->logger->log_debug("Services Enabled \n".print_r($services, true));
$hostRoles = $db->getAllHostsByComponent($clusterName);
if ($hostRoles["result"] != 0) {
$result["result"] = $hostRoles["result"];
$result["error"] = $hostRoles["error"];
return $result;
}
$order = array("sortColumn" => "cpuCount",
"sortOrder" => "ASC");
$allHosts = $db->getAllHostsInfo($clusterName,
array("=" => array ( "discoveryStatus" => "SUCCESS")), $order);
if ($allHosts["result"] != 0) {
$result["result"] = $allHosts["result"];
$result["error"] = $allHosts["error"];
return $result;
}
// convert host list to a map so thats easy to lookup
$hostInfoMap = $this->createHostToInfoMap($allHosts);
$allHostsToComponents = $db->getAllHostsToComponentMap($clusterName);
if ($allHostsToComponents["result"] != 0) {
$result["result"] = $allHostsToComponents["result"];
$result["error"] = $allHostsToComponents["error"];
return $result;
}
// filter host roles for client-only components
$ignoredComponents = array();
$allComponents = $db->getAllServiceComponentsList();
if ($allComponents["result"] == 0) {
if (isset($allComponents["services"])
&& is_array($allComponents["services"])) {
foreach ($allComponents["services"] as $svcName => $svcInfo) {
if (isset($svcInfo["components"])
&& is_array($svcInfo["components"])) {
foreach ($svcInfo["components"] as $compName => $compInfo) {
if (isset($compInfo["isClient"]) && $compInfo["isClient"]) {
$ignoredComponents[$compName] = TRUE;
} else if ($compName == "GANGLIA_MONITOR" || $compName == "GANGLIA2_MONITOR") {
$ignoredComponents[$compName] = TRUE;
}
}
}
}
}
}
foreach ($allHostsToComponents["hosts"] as $hostName => $compList) {
$newComps = array();
foreach ($compList["components"] as $compName) {
if (!isset($ignoredComponents[$compName])) {
array_push($newComps, $compName);
}
}
$allHostsToComponents["hosts"][$hostName]["components"] = $newComps;
}
$result["configs"] = array();
// set the num map/reduce tasks
// assuming that there is atleast one host
if (count($allHosts["hosts"]) == 1) {
// for single node install use 2 maps and 2 reduce slots
$this->logger->log_info("Single node install: Using Num Maps 2, Num Reduces 2");
$result["configs"]["mapred_map_tasks_max"] = 2;
$result["configs"]["mapred_red_tasks_max"] = 2;
} else {
$minCpuHost = $allHosts["hosts"][0];
$this->logger->log_info("Host Info with Min Cpu \n".print_r($minCpuHost, true));
$minCpus = $minCpuHost["cpuCount"];
$numMap = (int) (ceil ($minCpus/3 * 2 * 2)); // 2/3'rd of cpucount and multiply it by 2.
if ($numMap <= 0) {
$numMap = 1;
}
$numRed = ($minCpus * 2) - $numMap;
if ($numRed <= 0) {
$numRed = 1;
}
$this->logger->log_info("Num Maps ".$numMap ." Num Reduces ".$numRed);
$result["configs"]["mapred_map_tasks_max"] = $numMap;
$result["configs"]["mapred_red_tasks_max"] = $numRed;
}
$stackVersion = $db->getHadoopStackVersion($clusterName);
$hadoopVersion = $stackVersion['version'];
$nameNodeStr = "NAMENODE";
$sNameNodeStr = "SNAMENODE";
$computeMasterStr = "JOBTRACKER";
$hBaseMasterStr = "HBASE_MASTER";
$computeSlaveStr = "TASKTRACKER";
$hdfsSlaveStr = "DATANODE";
$hbaseSlaveStr = "HBASE_REGIONSERVER";
if ($hadoopVersion == AMBARI_HADOOP_2) {
$nameNodeStr = "NAMENODE2";
$sNameNodeStr = "SNAMENODE2";
$computeMasterStr = "RESOURCEMANAGER";
$hBaseMasterStr = "HBASE2_MASTER";
$computeSlaveStr = "NODEMANAGER";
$hdfsSlaveStr = "DATANODE2";
$hbaseSlaveStr = "HBASE2_REGIONSERVER";
}
/* suggest memory for all the needed master daemons */
/* assume MR and HDFS are always selected */
$nnHeap = $this->allocateHeapSizeForDaemon($nameNodeStr, $hostRoles,
$hostInfoMap, $allHostsToComponents, FALSE);
$result["configs"]["namenode_heapsize"] = $nnHeap;
/* suggest the jt heap size */
$jtHeap = $this->allocateHeapSizeForDaemon($computeMasterStr, $hostRoles,
$hostInfoMap, $allHostsToComponents, FALSE);
$result["configs"]["jtnode_heapsize"] = $jtHeap;
/* check if HBase is installed and then pick */
if (array_key_exists("HBASE", $services) || array_key_exists("HBASE2", $services)) {
$hbaseHeap = $this->allocateHeapSizeForDaemon($hBaseMasterStr, $hostRoles,
$hostInfoMap, $allHostsToComponents, FALSE);
$result["configs"]["hbase_master_heapsize"] = $hbaseHeap;
}
$heapSize = $this->allocateHeapSizeWithMax($hdfsSlaveStr, $hostRoles,
$hostInfoMap, $allHostsToComponents, TRUE, 2048);
// cap the datanode heap size and hadoop heap size
$result["configs"]["dtnode_heapsize"] = $heapSize;
$result["configs"]["hadoop_heapsize"] = $heapSize;
// TODO fix - this should be based on heap size divided by max task
// limit on the host
$heapSize = $this->allocateHeapSizeForDaemon($computeSlaveStr, $hostRoles,
$hostInfoMap, $allHostsToComponents, TRUE);
$mrHeapSizeWithMax = $this->allocateHeapSizeWithMax($computeSlaveStr, $hostRoles,
$hostInfoMap, $allHostsToComponents, TRUE, 1024);
$this->logger->log_info("Maxed Heap Size for MR Child opts ".$mrHeapSizeWithMax);
$result["configs"]["mapred_child_java_opts_sz"] = $mrHeapSizeWithMax;
if (array_key_exists("HBASE", $services) || array_key_exists("HBASE2", $services)) {
$heapSize = $this->allocateHeapSizeForDaemon($hbaseSlaveStr, $hostRoles,
$hostInfoMap, $allHostsToComponents, FALSE);
$result["configs"]["hbase_regionserver_heapsize"] = $heapSize;
}
/** TODO change this to be from the UI later **/
$hostname = strtolower(exec('hostname -f'));
$result["configs"]["jdk_location"] = "http://".$hostname."/downloads";
if ($updateDB) {
$this->logger->log_info("Updating suggested configs into DB");
$ret = $db->updateServiceConfigs($clusterName, $result["configs"]);
if ($ret["result"] != 0) {
$this->logger->log_error("Error updating suggested configs into DB"
. ", result=" . $ret["result"]
. ", error=" . $ret["error"]);
$result["result"] = $ret["result"];
$result["error"] = $ret["error"];
}
}
$this->logger->log_info("Calculated Config Parameters \n".print_r($result, true));
return $result;
}
/**
* Verify properties set in DB
* @param string $clusterName
* @param object $db - HMCDBAccessor
* @param mixed $configs
* array ( "prop_key1" => "prop_val1", ... )
* @return mixed
* array (
* "result" => 0,
* "error" => "",
* "cfgErrors" => array (
* "propKey" => array (
* "value" => "current val in DB",
* "recommendedValue" => $recoVal,
* "error" => "reason why this is an error"
* ),
* ...
* ),
* "cfgWarnings" => array (
* "propKey" => array (
* "value" => "current val in DB",
* "recommendedValue" => $recoVal,
* "error" => "reason why this is an warning"
* ),
* ...
* )
* )
*/
public function verifyProperties($clusterName, $db, $configs) {
$result = array();
$result["result"] = 0;
$result["error"] = "";
$servicesDBInfo = $db->getAllServicesInfo($clusterName);
if ($servicesDBInfo["result"] != 0) {
$result["result"] = $servicesDBInfo["result"];
$result["error"] = $servicesDBInfo["error"];
return $result;
}
$services_tmp = $servicesDBInfo["services"];
$services = $this->filterEnabledServices($services_tmp);
$this->logger->log_debug("Services Enabled \n".print_r($services, true));
$hostRoles = $db->getAllHostsByComponent($clusterName);
if ($hostRoles["result"] != 0) {
$result["result"] = $hostRoles["result"];
$result["error"] = $hostRoles["error"];
return $result;
}
$order = array("sortColumn" => "cpuCount",
"sortOrder" => "ASC");
$allHosts = $db->getAllHostsInfo($clusterName,
array("=" => array ( "discoveryStatus" => "SUCCESS")), $order);
if ($allHosts["result"] != 0) {
$result["result"] = $allHosts["result"];
$result["error"] = $allHosts["error"];
return $result;
}
// convert host list to a map so thats easy to lookup
$hostInfoMap = $this->createHostToInfoMap($allHosts);
$allHostsToComponents = $db->getAllHostsToComponentMap($clusterName);
if ($allHostsToComponents["result"] != 0) {
$result["result"] = $allHostsToComponents["result"];
$result["error"] = $allHostsToComponents["error"];
return $result;
}
$recommendedInfo = $this->suggestProperties($clusterName, $db, FALSE);
if ($recommendedInfo["result"] != 0) {
$result["result"] = $recommendedInfo["result"];
$result["error"] = $recommendedInfo["error"];
return $result;
}
$recommendedConfigs = $recommendedInfo["configs"];
// errors => array ( key => array ( value, recommended_value, reason ))
$cfgErrors = array();
$cfgWarnings = array();
// verify map and reduce tasks max settings
if (isset($configs["mapred_map_tasks_max"])
&& isset($configs["mapred_red_tasks_max"])) {
if ($configs["mapred_map_tasks_max"] == 0
|| $configs["mapred_red_tasks_max"] == 0) {
$reason = "Value cannot be 0";
if ($configs["mapred_map_tasks_max"] == 0) {
$cfgErrors["mapred_map_tasks_max"] = array ( "value" => 0,
"recommendedValue" => $recommendedConfigs["mapred_map_tasks_max"],
"error" => $reason);
}
if ($configs["mapred_red_tasks_max"] == 0) {
$cfgErrors["mapred_red_tasks_max"] = array ( "value" => 0,
"recommendedValue" => $recommendedConfigs["mapred_red_tasks_max"],
"error" => $reason);
}
}
if ($configs["mapred_map_tasks_max"] >
$recommendedConfigs["mapred_map_tasks_max"]) {
$cfgWarnings["mapred_map_tasks_max"] = array (
"value" => $configs["mapred_map_tasks_max"],
"recommendedValue" => $recommendedConfigs["mapred_map_tasks_max"],
"error" => "Value greater than recommended");
}
if ($configs["mapred_red_tasks_max"] >
$recommendedConfigs["mapred_red_tasks_max"]) {
$cfgWarnings["mapred_red_tasks_max"] = array (
"value" => $configs["mapred_red_tasks_max"],
"recommendedValue" => $recommendedConfigs["mapred_red_tasks_max"],
"error" => "Value greater than recommended");
}
}
$stackVersion = $db->getHadoopStackVersion($clusterName);
$hadoopVersion = $stackVersion['version'];
$memProps = array();
if ($hadoopVersion == AMBARI_HADOOP_1) {
$memProps = array (
"namenode_heapsize" => array ( "role" => "NAMENODE", "32bit" => FALSE),
"jtnode_heapsize" => array ( "role" => "JOBTRACKER", "32bit" => FALSE),
"dtnode_heapsize" => array ( "role" => "DATANODE", "32bit" => TRUE),
"hadoop_heapsize" => array ( "role" => "DATANODE", "32bit" => TRUE),
"mapred_child_java_opts_sz" => array ( "role" => "TASKTRACKER", "32bit" => TRUE)
);
} else if ($hadoopVersion == AMBARI_HADOOP_2) {
$memProps = array (
"namenode_heapsize" => array ( "role" => "NAMENODE2", "32bit" => FALSE),
"resourcemanager_heapsize" => array ( "role" => "RESOURCEMANAGER", "32bit" => FALSE),
"dtnode_heapsize" => array ( "role" => "DATANODE2", "32bit" => TRUE),
"hadoop_heapsize" => array ( "role" => "DATANODE2", "32bit" => TRUE)
);
}
$hBaseStr = "HBASE";
$hBaseMasterStr = "HBASE_MASTER";
$hbaseSlaveStr = "HBASE2_REGIONSERVER";
if ($hadoopVersion == "AMBARI_HADOOP_2") {
$hBaseStr = "HBASE2";
$hBaseMasterStr = "HBASE2_MASTER";
$hbaseSlaveStr = "HBASE2_REGIONSERVER";
}
if (array_key_exists($hBaseStr, $services)) {
$memProps["hbase_master_heapsize"] =
array ( "role" => $hBaseMasterStr, "32bit" => FALSE);
$memProps["hbase_regionserver_heapsize"] =
array ( "role" => $hbaseSlaveStr, "32bit" => FALSE);
}
foreach ($memProps as $prop => $propInfo) {
if (!isset($configs[$prop])) {
continue;
}
if ($configs[$prop] < 256) {
$reason = "Value less than min 256M";
$cfgErrors[$prop] = array (
"value" => $configs[$prop],
"recommendedValue" => $recommendedConfigs[$prop],
"error" => $reason
);
continue;
}
if ($configs[$prop] >
$recommendedConfigs[$prop]) {
$maxHeap = $this->getMaxHeapSizeForDaemon($propInfo["role"], $hostRoles,
$hostInfoMap, $allHostsToComponents, $propInfo["32bit"]);
if ($configs[$prop] > $maxHeap) {
$reason = "Value greater than mem limit allowed";
$cfgErrors[$prop] = array (
"value" => $configs[$prop],
"recommendedValue" => $recommendedConfigs[$prop],
"error" => $reason
);
} else {
$reason = "Value greater than recommended mem limit";
$cfgWarnings[$prop] = array (
"value" => $configs[$prop],
"recommendedValue" => $recommendedConfigs[$prop],
"error" => $reason
);
}
}
}
$result = count($cfgErrors);
$error = "";
if ($result != 0 ) {
$error = "Invalid Configs";
}
return array ("result" => $result, "error" => $error,
"cfgErrors" => $cfgErrors,
"cfgWarnings" => $cfgWarnings);
}
}
?>