Repository: ambari
Updated Branches:
  refs/heads/trunk 6e9b4be25 -> 8258cf893


AMBARI-19096. HDP 3.0 TP - create Service Advisor for ZK (alejandro)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/8258cf89
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/8258cf89
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/8258cf89

Branch: refs/heads/trunk
Commit: 8258cf893175342142523ec0001b9f4d3f09ce3f
Parents: 6e9b4be
Author: Alejandro Fernandez <afernan...@hortonworks.com>
Authored: Mon Dec 19 14:59:40 2016 -0800
Committer: Alejandro Fernandez <afernan...@hortonworks.com>
Committed: Mon Jan 9 11:46:36 2017 -0800

----------------------------------------------------------------------
 .../ZOOKEEPER/3.4.9/service_advisor.py          | 143 ++++++++
 .../src/main/resources/scripts/stack_advisor.py |   9 +-
 .../stacks/BIGTOP/0.8/services/stack_advisor.py | 125 +++++--
 .../stacks/HDP/2.0.6/services/stack_advisor.py  | 192 ++++------
 .../stacks/HDP/2.1/services/stack_advisor.py    |  63 +++-
 .../stacks/HDP/2.2/services/stack_advisor.py    |  69 +++-
 .../stacks/HDPWIN/2.1/services/stack_advisor.py |  68 +++-
 .../stacks/HDPWIN/2.2/services/stack_advisor.py |  74 +++-
 .../main/resources/stacks/service_advisor.py    |  65 ++--
 .../src/main/resources/stacks/stack_advisor.py  | 367 ++++++++++++++++++-
 .../2.2/common/test_stack_advisor_perf.py       |  22 +-
 .../stacks/2.3/common/test_stack_advisor.py     |  30 +-
 12 files changed, 965 insertions(+), 262 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/8258cf89/ambari-server/src/main/resources/common-services/ZOOKEEPER/3.4.9/service_advisor.py
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/resources/common-services/ZOOKEEPER/3.4.9/service_advisor.py
 
b/ambari-server/src/main/resources/common-services/ZOOKEEPER/3.4.9/service_advisor.py
new file mode 100644
index 0000000..82316f4
--- /dev/null
+++ 
b/ambari-server/src/main/resources/common-services/ZOOKEEPER/3.4.9/service_advisor.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env ambari-python-wrap
+"""
+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.
+"""
+
+# Python imports
+import imp
+import os
+import traceback
+import inspect
+
+# Local imports
+from resource_management.core.logger import Logger
+
+
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+STACKS_DIR = os.path.join(SCRIPT_DIR, '../../../stacks/')
+PARENT_FILE = os.path.join(STACKS_DIR, 'service_advisor.py')
+
+try:
+  with open(PARENT_FILE, 'rb') as fp:
+    service_advisor = imp.load_module('service_advisor', fp, PARENT_FILE, 
('.py', 'rb', imp.PY_SOURCE))
+except Exception as e:
+  traceback.print_exc()
+  print "Failed to load parent"
+
+
+class ZookeeperServiceAdvisor(service_advisor.ServiceAdvisor):
+
+  def __init__(self, *args, **kwargs):
+    self.as_super = super(ZookeeperServiceAdvisor, self)
+    self.as_super.__init__(*args, **kwargs)
+
+    self.modifyMastersWithMultipleInstances()
+    self.modifyCardinalitiesDict()
+    self.modifyHeapSizeProperties()
+
+  def modifyMastersWithMultipleInstances(self):
+    """
+    Modify the set of masters with multiple instances.
+    Must be overriden in child class.
+    """
+    self.mastersWithMultipleInstances.add("ZOOKEEPER_SERVER")
+
+  def modifyCardinalitiesDict(self):
+    """
+    Modify the dictionary of cardinalities.
+    Must be overriden in child class.
+    """
+    self.cardinalitiesDict["ZOOKEEPER_SERVER"] = {"min": 3}
+
+  def modifyHeapSizeProperties(self):
+    """
+    Modify the dictionary of heap size properties.
+    Must be overriden in child class.
+    """
+    self.heap_size_properties = {"ZOOKEEPER_SERVER": [{"config-name": 
"zookeeper-env",
+                                                       "property": 
"zk_server_heapsize",
+                                                       "default": "1024m"}]}
+
+  def getServiceComponentLayoutValidations(self, services, hosts):
+    """
+    Get a list of errors. Zookeeper does not have any validations in this 
version.
+    """
+    service_name = services["services"][0]["StackServices"]["service_name"]
+    Logger.info("Class: %s, Method: %s. Validating Service Component Layout 
for Service: %s." %
+                (self.__class__.__name__, inspect.stack()[0][3], service_name))
+    return self.as_super.getServiceComponentLayoutValidations(services, hosts)
+
+  def getServiceConfigurationRecommendations(self, configurations, 
clusterData, services, hosts):
+    """
+    Recommend configurations to set. Zookeeper does not have any 
recommendations in this version.
+    """
+    service_name = services["services"][0]["StackServices"]["service_name"]
+    Logger.info("Class: %s, Method: %s. Recommending Service Configurations 
for Service: %s." %
+                (self.__class__.__name__, inspect.stack()[0][3], service_name))
+
+    self.recommendConfigurations(configurations, clusterData, services, hosts)
+
+  def recommendConfigurations(self, configurations, clusterData, services, 
hosts):
+    """
+    Recommend configurations for this service.
+    """
+    service_name = services["services"][0]["StackServices"]["service_name"]
+    Logger.info("Class: %s, Method: %s. Recommending Service Configurations 
for Service: %s." %
+                (self.__class__.__name__, inspect.stack()[0][3], service_name))
+
+    Logger.info("Setting zoo.cfg to default dataDir to /hadoop/zookeeper on 
the best matching mount")
+
+    zk_mount_properties = [
+      ("dataDir", "ZOOKEEPER_SERVER", "/hadoop/zookeeper", "single"),
+    ]
+    self.updateMountProperties("zoo.cfg", zk_mount_properties, configurations, 
services, hosts)
+
+  def getServiceConfigurationsValidationItems(self, configurations, 
recommendedDefaults, services, hosts):
+    """
+    Validate configurations for the service. Return a list of errors.
+    """
+    service_name = services["services"][0]["StackServices"]["service_name"]
+    Logger.info("Class: %s, Method: %s. Validating Configurations for Service: 
%s." %
+                (self.__class__.__name__, inspect.stack()[0][3], service_name))
+
+    items = []
+
+    # Example of validating by calling helper methods
+    '''
+    configType = "zookeeper-env"
+    method = self.someMethodInThisClass
+    resultItems = self.validateConfigurationsForSite(configurations, 
recommendedDefaults, services, hosts, configType, method)
+    items.extend(resultItems)
+
+    method = self.anotherMethodInThisClass
+    resultItems = self.validateConfigurationsForSite(configurations, 
recommendedDefaults, services, hosts, configType, method)
+    items.extend(resultItems)
+    '''
+
+    return items
+
+  '''
+  def someMethodInThisClass(self, properties, recommendedDefaults, 
configurations, services, hosts):
+    validationItems = []
+    validationItems.append({"config-name": "zookeeper-env", "item": 
self.getErrorItem("My custom message 1")})
+    return self.toConfigurationValidationProblems(validationItems, 
"zookeeper-env")
+
+  def anotherMethodInThisClass(self, properties, recommendedDefaults, 
configurations, services, hosts):
+    validationItems = []
+    validationItems.append({"config-name": "zookeeper-env", "item": 
self.getErrorItem("My custom message 2")})
+    return self.toConfigurationValidationProblems(validationItems, 
"zookeeper-env")
+  '''
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/8258cf89/ambari-server/src/main/resources/scripts/stack_advisor.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/scripts/stack_advisor.py 
b/ambari-server/src/main/resources/scripts/stack_advisor.py
index abfab87..1d9b4a2 100755
--- a/ambari-server/src/main/resources/scripts/stack_advisor.py
+++ b/ambari-server/src/main/resources/scripts/stack_advisor.py
@@ -134,10 +134,11 @@ def instantiateStackAdvisor(stackName, stackVersion, 
parentVersions):
     try:
       path = STACK_ADVISOR_IMPL_PATH_TEMPLATE.format(stackName, version)
 
-      with open(path, 'rb') as fp:
-        stack_advisor = imp.load_module('stack_advisor_impl', fp, path, 
('.py', 'rb', imp.PY_SOURCE))
-      className = STACK_ADVISOR_IMPL_CLASS_TEMPLATE.format(stackName, 
version.replace('.', ''))
-      print "StackAdvisor implementation for stack {0}, version {1} was 
loaded".format(stackName, version)
+      if os.path.isfile(path):
+        with open(path, 'rb') as fp:
+          stack_advisor = imp.load_module('stack_advisor_impl', fp, path, 
('.py', 'rb', imp.PY_SOURCE))
+        className = STACK_ADVISOR_IMPL_CLASS_TEMPLATE.format(stackName, 
version.replace('.', ''))
+        print "StackAdvisor implementation for stack {0}, version {1} was 
loaded".format(stackName, version)
     except IOError: # file not found
       traceback.print_exc()
       print "StackAdvisor implementation for stack {0}, version {1} was not 
found".format(stackName, version)

http://git-wip-us.apache.org/repos/asf/ambari/blob/8258cf89/ambari-server/src/main/resources/stacks/BIGTOP/0.8/services/stack_advisor.py
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/resources/stacks/BIGTOP/0.8/services/stack_advisor.py 
b/ambari-server/src/main/resources/stacks/BIGTOP/0.8/services/stack_advisor.py
index 701d0d4..5172042 100644
--- 
a/ambari-server/src/main/resources/stacks/BIGTOP/0.8/services/stack_advisor.py
+++ 
b/ambari-server/src/main/resources/stacks/BIGTOP/0.8/services/stack_advisor.py
@@ -17,13 +17,68 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 """
 
+# Python Imports
 import re
 from math import ceil
 
+# Local Imports
+from resource_management.core.logger import Logger
 from stack_advisor import DefaultStackAdvisor
 
 class BaseBIGTOP08StackAdvisor(DefaultStackAdvisor):
 
+
+  def __init__(self):
+    super(BaseBIGTOP08StackAdvisor, self).__init__()
+    Logger.initialize_logger()
+
+    self.modifyMastersWithMultipleInstances()
+    self.modifyCardinalitiesDict()
+    self.modifyHeapSizeProperties()
+    self.modifyNotValuableComponents()
+    self.modifyComponentsNotPreferableOnServer()
+
+  def modifyMastersWithMultipleInstances(self):
+    """
+    Modify the set of masters with multiple instances.
+    Must be overriden in child class.
+    """
+    self.mastersWithMultipleInstances |= set(['ZOOKEEPER_SERVER', 
'HBASE_MASTER'])
+
+  def modifyCardinalitiesDict(self):
+    """
+    Modify the dictionary of cardinalities.
+    Must be overriden in child class.
+    """
+    self.cardinalitiesDict.update(
+      {
+        'ZOOKEEPER_SERVER': {"min": 3},
+        'HBASE_MASTER': {"min": 1}
+      }
+    )
+
+  def modifyHeapSizeProperties(self):
+    """
+    Modify the dictionary of heap size properties.
+    Must be overriden in child class.
+    """
+    # Nothing to do
+    pass
+
+  def modifyNotValuableComponents(self):
+    """
+    Modify the set of components whose host assignment is based on other 
services.
+    Must be overriden in child class.
+    """
+    self.notValuableComponents |= set(['JOURNALNODE', 'ZKFC', 
'GANGLIA_MONITOR'])
+
+  def modifyComponentsNotPreferableOnServer(self):
+    """
+    Modify the set of components that are not preferable on the server.
+    Must be overriden in child class.
+    """
+    self.notPreferableOnServerComponents |= set(['GANGLIA_SERVER'])
+
   def getComponentLayoutValidations(self, services, hosts):
     """Returns array of Validation objects about issues with hostnames 
components assigned to"""
     items = []
@@ -275,21 +330,7 @@ class BaseBIGTOP08StackAdvisor(DefaultStackAdvisor):
                         {"config-name": 
'yarn.scheduler.maximum-allocation-mb', "item": 
self.validatorLessThenDefaultValue(properties, recommendedDefaults, 
'yarn.scheduler.maximum-allocation-mb')} ]
     return self.toConfigurationValidationProblems(validationItems, "yarn-site")
 
-  def getMastersWithMultipleInstances(self):
-    return ['ZOOKEEPER_SERVER', 'HBASE_MASTER']
-
-  def getNotValuableComponents(self):
-    return ['JOURNALNODE', 'ZKFC', 'GANGLIA_MONITOR']
-
-  def getNotPreferableOnServerComponents(self):
-    return ['GANGLIA_SERVER']
-
-  def getCardinalitiesDict(self):
-    return {
-      'ZOOKEEPER_SERVER': {"min": 3},
-      'HBASE_MASTER': {"min": 1},
-      }
-
+  # TODO, move to Service Advisors.
   def getComponentLayoutSchemes(self):
     return {
       'NAMENODE': {"else": 0},
@@ -308,6 +349,53 @@ class BaseBIGTOP08StackAdvisor(DefaultStackAdvisor):
 
 class BIGTOP08StackAdvisor(BaseBIGTOP08StackAdvisor):
 
+  def __init__(self):
+    super(BIGTOP08StackAdvisor, self).__init__()
+
+    self.modifyMastersWithMultipleInstances()
+    self.modifyCardinalitiesDict()
+    self.modifyHeapSizeProperties()
+    self.modifyNotValuableComponents()
+    self.modifyComponentsNotPreferableOnServer()
+
+  def modifyMastersWithMultipleInstances(self):
+    """
+    Modify the set of masters with multiple instances.
+    Must be overriden in child class.
+    """
+    # Nothing to do
+    pass
+
+  def modifyCardinalitiesDict(self):
+    """
+    Modify the dictionary of cardinalities.
+    Must be overriden in child class.
+    """
+    # Nothing to do
+    pass
+
+  def modifyHeapSizeProperties(self):
+    """
+    Modify the dictionary of heap size properties.
+    Must be overriden in child class.
+    """
+    # Nothing to do
+    pass
+
+  def modifyNotValuableComponents(self):
+    """
+    Modify the set of components whose host assignment is based on other 
services.
+    Must be overriden in child class.
+    """
+    self.notValuableComponents |= set(['APP_TIMELINE_SERVER'])
+
+  def modifyComponentsNotPreferableOnServer(self):
+    """
+    Modify the set of components that are not preferable on the server.
+    Must be overriden in child class.
+    """
+    self.notPreferableOnServerComponents |= set(['STORM_UI_SERVER', 
'DRPC_SERVER', 'STORM_REST_API', 'NIMBUS'])
+
   def getServiceConfigurationRecommenderDict(self):
     parentRecommendConfDict = super(BIGTOP08StackAdvisor, 
self).getServiceConfigurationRecommenderDict()
     childRecommendConfDict = {
@@ -342,12 +430,7 @@ class BIGTOP08StackAdvisor(BaseBIGTOP08StackAdvisor):
                    "-server -Xmx" + str(int(0.8 * clusterData["amMemory"]))
                    + "m -Djava.net.preferIPv4Stack=true -XX:+UseNUMA 
-XX:+UseParallelGC")
 
-  def getNotPreferableOnServerComponents(self):
-    return ['STORM_UI_SERVER', 'DRPC_SERVER', 'STORM_REST_API', 'NIMBUS', 
'GANGLIA_SERVER']
-
-  def getNotValuableComponents(self):
-    return ['JOURNALNODE', 'ZKFC', 'GANGLIA_MONITOR', 'APP_TIMELINE_SERVER']
-
+  # TODO, move to Service Advisors.
   def getComponentLayoutSchemes(self):
     parentSchemes = super(BIGTOP08StackAdvisor, 
self).getComponentLayoutSchemes()
     childSchemes = {

http://git-wip-us.apache.org/repos/asf/ambari/blob/8258cf89/ambari-server/src/main/resources/stacks/HDP/2.0.6/services/stack_advisor.py
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/resources/stacks/HDP/2.0.6/services/stack_advisor.py 
b/ambari-server/src/main/resources/stacks/HDP/2.0.6/services/stack_advisor.py
index 0c02eab..9b217b3 100644
--- 
a/ambari-server/src/main/resources/stacks/HDP/2.0.6/services/stack_advisor.py
+++ 
b/ambari-server/src/main/resources/stacks/HDP/2.0.6/services/stack_advisor.py
@@ -17,17 +17,17 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 """
 
+# Python Imports
 import re
 import os
 import sys
 import socket
-
 from math import ceil, floor, log
 
+# Local Imports
 from resource_management.core.logger import Logger
 from resource_management.libraries.functions.mounted_dirs_helper import 
get_mounts_with_multiple_data_dirs
 from resource_management.libraries.functions.data_structure_utils import 
get_from_dict
-
 from stack_advisor import DefaultStackAdvisor
 
 
@@ -37,6 +37,53 @@ class HDP206StackAdvisor(DefaultStackAdvisor):
     super(HDP206StackAdvisor, self).__init__()
     Logger.initialize_logger()
 
+    self.modifyMastersWithMultipleInstances()
+    self.modifyCardinalitiesDict()
+    self.modifyHeapSizeProperties()
+    self.modifyNotValuableComponents()
+    self.modifyComponentsNotPreferableOnServer()
+
+  def modifyMastersWithMultipleInstances(self):
+    """
+    Modify the set of masters with multiple instances.
+    Must be overriden in child class.
+    """
+    self.mastersWithMultipleInstances |= set(['ZOOKEEPER_SERVER', 
'HBASE_MASTER'])
+
+  def modifyCardinalitiesDict(self):
+    """
+    Modify the dictionary of cardinalities.
+    Must be overriden in child class.
+    """
+    self.cardinalitiesDict.update(
+      {
+        'ZOOKEEPER_SERVER': {"min": 3},
+        'HBASE_MASTER': {"min": 1},
+      }
+    )
+
+  def modifyHeapSizeProperties(self):
+    """
+    Modify the dictionary of heap size properties.
+    Must be overriden in child class.
+    """
+    # Nothing to do
+    pass
+
+  def modifyNotValuableComponents(self):
+    """
+    Modify the set of components whose host assignment is based on other 
services.
+    Must be overriden in child class.
+    """
+    self.notValuableComponents |= set(['JOURNALNODE', 'ZKFC', 
'GANGLIA_MONITOR'])
+
+  def modifyComponentsNotPreferableOnServer(self):
+    """
+    Modify the set of components that are not preferable on the server.
+    Must be overriden in child class.
+    """
+    self.notPreferableOnServerComponents |= set(['GANGLIA_SERVER', 
'METRICS_COLLECTOR'])
+
   def getComponentLayoutValidations(self, services, hosts):
     """Returns array of Validation objects about issues with hostnames 
components assigned to"""
     items = super(HDP206StackAdvisor, 
self).getComponentLayoutValidations(services, hosts)
@@ -953,6 +1000,7 @@ class HDP206StackAdvisor(DefaultStackAdvisor):
                               and hostname in 
componentEntry["StackServiceComponents"]["hostnames"]])
     return components
 
+  # TODO, move this to a generic stack advisor.
   def getZKHostPortString(self, services, include_port=True):
     """
     Returns the comma delimited string of zookeeper server host with the 
configure port installed in a cluster
@@ -978,6 +1026,7 @@ class HDP206StackAdvisor(DefaultStackAdvisor):
       zookeeper_host_port = ",".join(zookeeper_host_port_arr)
     return zookeeper_host_port
 
+  # TODO, move this to a generic stack advisor
   def getZKPort(self, services):
     zookeeper_port = '2181'     #default port
     if 'zoo.cfg' in services['configurations'] and ('clientPort' in 
services['configurations']['zoo.cfg']['properties']):
@@ -1414,7 +1463,7 @@ class HDP206StackAdvisor(DefaultStackAdvisor):
                 if collectorHostName in 
component["StackServiceComponents"]["hostnames"]:
                   
hostComponents.append(component["StackServiceComponents"]["component_name"])
 
-          requiredMemory = getMemorySizeRequired(hostComponents, 
configurations)
+          requiredMemory = self.getMemorySizeRequired(services, 
hostComponents, configurations)
           unusedMemory = host["Hosts"]["total_mem"] * 1024 - requiredMemory # 
in bytes
           collector_needs_increase = collector_heapsize * mb < 16 * gb
 
@@ -1429,6 +1478,33 @@ class HDP206StackAdvisor(DefaultStackAdvisor):
     pass
     return self.toConfigurationValidationProblems(validationItems, "ams-env")
 
+  def getMemorySizeRequired(self, services, components, configurations):
+    totalMemoryRequired = 512*1024*1024 # 512Mb for OS needs
+    # Dictionary from component name to list of dictionary with keys: 
config-name, property, default.
+    heapSizeProperties = self.get_heap_size_properties(services)
+    for component in components:
+      if component in heapSizeProperties.keys():
+        heapSizePropertiesForComp = heapSizeProperties[component]
+        for heapSizeProperty in heapSizePropertiesForComp:
+          try:
+            properties = 
configurations[heapSizeProperty["config-name"]]["properties"]
+            heapsize = properties[heapSizeProperty["property"]]
+          except KeyError:
+            heapsize = heapSizeProperty["default"]
+
+          # Assume Mb if no modifier
+          if len(heapsize) > 1 and heapsize[-1] in '0123456789':
+            heapsize = str(heapsize) + "m"
+
+          totalMemoryRequired += formatXmxSizeToBytes(heapsize)
+      else:
+        if component == "METRICS_MONITOR" or "CLIENT" in component:
+          heapsize = '512m'
+        else:
+          heapsize = '1024m'
+        totalMemoryRequired += formatXmxSizeToBytes(heapsize)
+    return totalMemoryRequired
+
   def getPreferredMountPoints(self, hostInfo):
 
     # '/etc/resolv.conf', '/etc/hostname', '/etc/hosts' are docker specific 
mount points
@@ -1707,21 +1783,7 @@ class HDP206StackAdvisor(DefaultStackAdvisor):
         return dataNodeHosts
     return []
 
-  def getMastersWithMultipleInstances(self):
-    return ['ZOOKEEPER_SERVER', 'HBASE_MASTER']
-
-  def getNotValuableComponents(self):
-    return ['JOURNALNODE', 'ZKFC', 'GANGLIA_MONITOR']
-
-  def getNotPreferableOnServerComponents(self):
-    return ['GANGLIA_SERVER', 'METRICS_COLLECTOR']
-
-  def getCardinalitiesDict(self):
-    return {
-      'ZOOKEEPER_SERVER': {"min": 3},
-      'HBASE_MASTER': {"min": 1},
-      }
-
+  # TODO, move to Service Advisors.
   def getComponentLayoutSchemes(self):
     return {
       'NAMENODE': {"else": 0},
@@ -2063,99 +2125,5 @@ def getMountPointForDir(dir, mountPoints):
 
   return bestMountFound
 
-def getHeapsizeProperties():
-  return { "NAMENODE": [{"config-name": "hadoop-env",
-                         "property": "namenode_heapsize",
-                         "default": "1024m"}],
-           "SECONDARY_NAMENODE": [{"config-name": "hadoop-env",
-                         "property": "namenode_heapsize",
-                         "default": "1024m"}],
-           "DATANODE": [{"config-name": "hadoop-env",
-                         "property": "dtnode_heapsize",
-                         "default": "1024m"}],
-           "REGIONSERVER": [{"config-name": "hbase-env",
-                             "property": "hbase_regionserver_heapsize",
-                             "default": "1024m"}],
-           "HBASE_MASTER": [{"config-name": "hbase-env",
-                             "property": "hbase_master_heapsize",
-                             "default": "1024m"}],
-           "HIVE_CLIENT": [{"config-name": "hive-env",
-                            "property": "hive.client.heapsize",
-                            "default": "1024"}],
-           "HIVE_METASTORE": [{"config-name": "hive-env",
-                            "property": "hive.metastore.heapsize",
-                            "default": "1024"}],
-           "HIVE_SERVER": [{"config-name": "hive-env",
-                               "property": "hive.heapsize",
-                               "default": "1024"}],
-           "HISTORYSERVER": [{"config-name": "mapred-env",
-                              "property": "jobhistory_heapsize",
-                              "default": "1024m"}],
-           "OOZIE_SERVER": [{"config-name": "oozie-env",
-                             "property": "oozie_heapsize",
-                             "default": "1024m"}],
-           "RESOURCEMANAGER": [{"config-name": "yarn-env",
-                                "property": "resourcemanager_heapsize",
-                                "default": "1024m"}],
-           "NODEMANAGER": [{"config-name": "yarn-env",
-                            "property": "nodemanager_heapsize",
-                            "default": "1024m"}],
-           "APP_TIMELINE_SERVER": [{"config-name": "yarn-env",
-                                    "property": "apptimelineserver_heapsize",
-                                    "default": "1024m"}],
-           "ZOOKEEPER_SERVER": [{"config-name": "zookeeper-env",
-                                 "property": "zk_server_heapsize",
-                                 "default": "1024m"}],
-           "METRICS_COLLECTOR": [{"config-name": "ams-hbase-env",
-                                   "property": "hbase_master_heapsize",
-                                   "default": "1024"},
-                                 {"config-name": "ams-hbase-env",
-                                  "property": "hbase_regionserver_heapsize",
-                                  "default": "1024"},
-                                 {"config-name": "ams-env",
-                                   "property": "metrics_collector_heapsize",
-                                   "default": "512"}],
-           "ATLAS_SERVER": [{"config-name": "atlas-env",
-                             "property": "atlas_server_xmx",
-                             "default": "2048"}],
-           "LOGSEARCH_SERVER": [{"config-name": "logsearch-env",
-                            "property": "logsearch_app_max_memory",
-                            "default": "1024"}],
-           "LOGSEARCH_LOGFEEDER": [{"config-name": "logfeeder-env",
-                            "property": "logfeeder_max_mem",
-                            "default": "512"}],
-           "SPARK_JOBHISTORYSERVER": [{"config-name": "spark-env",
-                                 "property": "spark_daemon_memory",
-                                 "default": "1024"}],
-           "SPARK2_JOBHISTORYSERVER": [{"config-name": "spark2-env",
-                                       "property": "spark_daemon_memory",
-                                       "default": "1024"}]
-           }
-
-def getMemorySizeRequired(components, configurations):
-  totalMemoryRequired = 512*1024*1024 # 512Mb for OS needs
-  for component in components:
-    if component in getHeapsizeProperties().keys():
-      heapSizeProperties = getHeapsizeProperties()[component]
-      for heapSizeProperty in heapSizeProperties:
-        try:
-          properties = 
configurations[heapSizeProperty["config-name"]]["properties"]
-          heapsize = properties[heapSizeProperty["property"]]
-        except KeyError:
-          heapsize = heapSizeProperty["default"]
-
-        # Assume Mb if no modifier
-        if len(heapsize) > 1 and heapsize[-1] in '0123456789':
-          heapsize = str(heapsize) + "m"
-
-        totalMemoryRequired += formatXmxSizeToBytes(heapsize)
-    else:
-      if component == "METRICS_MONITOR" or "CLIENT" in component:
-        heapsize = '512m'
-      else:
-        heapsize = '1024m'
-      totalMemoryRequired += formatXmxSizeToBytes(heapsize)
-  return totalMemoryRequired
-
 def round_to_n(mem_size, n=128):
   return int(round(mem_size / float(n))) * int(n)

http://git-wip-us.apache.org/repos/asf/ambari/blob/8258cf89/ambari-server/src/main/resources/stacks/HDP/2.1/services/stack_advisor.py
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/resources/stacks/HDP/2.1/services/stack_advisor.py 
b/ambari-server/src/main/resources/stacks/HDP/2.1/services/stack_advisor.py
index f34f5a1..6ae29cb 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.1/services/stack_advisor.py
+++ b/ambari-server/src/main/resources/stacks/HDP/2.1/services/stack_advisor.py
@@ -17,8 +17,63 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 """
 
+# Local Imports
+from resource_management.core.logger import Logger
+
+
 class HDP21StackAdvisor(HDP206StackAdvisor):
 
+  def __init__(self):
+    super(HDP21StackAdvisor, self).__init__()
+    Logger.initialize_logger()
+
+    self.modifyMastersWithMultipleInstances()
+    self.modifyCardinalitiesDict()
+    self.modifyHeapSizeProperties()
+    self.modifyNotValuableComponents()
+    self.modifyComponentsNotPreferableOnServer()
+
+  def modifyMastersWithMultipleInstances(self):
+    """
+    Modify the set of masters with multiple instances.
+    Must be overriden in child class.
+    """
+    self.mastersWithMultipleInstances |= set(['ZOOKEEPER_SERVER', 
'HBASE_MASTER'])
+
+  def modifyCardinalitiesDict(self):
+    """
+    Modify the dictionary of cardinalities.
+    Must be overriden in child class.
+    """
+    self.cardinalitiesDict.update(
+      {
+        'ZOOKEEPER_SERVER': {"min": 3},
+        'HBASE_MASTER': {"min": 1},
+      }
+    )
+
+  def modifyHeapSizeProperties(self):
+    """
+    Modify the dictionary of heap size properties.
+    Must be overriden in child class.
+    """
+    # Nothing to do
+    pass
+
+  def modifyNotValuableComponents(self):
+    """
+    Modify the set of components whose host assignment is based on other 
services.
+    Must be overriden in child class.
+    """
+    self.notValuableComponents |= set(['JOURNALNODE', 'ZKFC', 
'GANGLIA_MONITOR', 'APP_TIMELINE_SERVER'])
+
+  def modifyComponentsNotPreferableOnServer(self):
+    """
+    Modify the set of components that are not preferable on the server.
+    Must be overriden in child class.
+    """
+    self.notPreferableOnServerComponents |= set(['STORM_UI_SERVER', 
'DRPC_SERVER', 'STORM_REST_API', 'NIMBUS', 'GANGLIA_SERVER', 
'METRICS_COLLECTOR'])
+
   def getServiceConfigurationRecommenderDict(self):
     parentRecommendConfDict = super(HDP21StackAdvisor, 
self).getServiceConfigurationRecommenderDict()
     childRecommendConfDict = {
@@ -155,13 +210,7 @@ class HDP21StackAdvisor(HDP206StackAdvisor):
     if recommended_tez_queue is not None:
       putTezProperty("tez.queue.name", recommended_tez_queue)
 
-
-  def getNotPreferableOnServerComponents(self):
-    return ['STORM_UI_SERVER', 'DRPC_SERVER', 'STORM_REST_API', 'NIMBUS', 
'GANGLIA_SERVER', 'METRICS_COLLECTOR']
-
-  def getNotValuableComponents(self):
-    return ['JOURNALNODE', 'ZKFC', 'GANGLIA_MONITOR', 'APP_TIMELINE_SERVER']
-
+  # TODO, move to Service Advisors.
   def getComponentLayoutSchemes(self):
     parentSchemes = super(HDP21StackAdvisor, self).getComponentLayoutSchemes()
     childSchemes = {

http://git-wip-us.apache.org/repos/asf/ambari/blob/8258cf89/ambari-server/src/main/resources/stacks/HDP/2.2/services/stack_advisor.py
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/resources/stacks/HDP/2.2/services/stack_advisor.py 
b/ambari-server/src/main/resources/stacks/HDP/2.2/services/stack_advisor.py
index cf7134e..b601179 100644
--- a/ambari-server/src/main/resources/stacks/HDP/2.2/services/stack_advisor.py
+++ b/ambari-server/src/main/resources/stacks/HDP/2.2/services/stack_advisor.py
@@ -17,6 +17,7 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 """
 
+# Python Imports
 import math
 from math import floor
 from urlparse import urlparse
@@ -26,9 +27,62 @@ import socket
 import re
 import xml.etree.ElementTree as ET
 
+# Local Imports
+from resource_management.core.logger import Logger
 
 class HDP22StackAdvisor(HDP21StackAdvisor):
 
+  def __init__(self):
+    super(HDP22StackAdvisor, self).__init__()
+    Logger.initialize_logger()
+
+    self.modifyMastersWithMultipleInstances()
+    self.modifyCardinalitiesDict()
+    self.modifyHeapSizeProperties()
+    self.modifyNotValuableComponents()
+    self.modifyComponentsNotPreferableOnServer()
+
+  def modifyMastersWithMultipleInstances(self):
+    """
+    Modify the set of masters with multiple instances.
+    Must be overriden in child class.
+    """
+    self.mastersWithMultipleInstances |= set(['METRICS_COLLECTOR'])
+
+  def modifyCardinalitiesDict(self):
+    """
+    Modify the dictionary of cardinalities.
+    Must be overriden in child class.
+    """
+    self.cardinalitiesDict.update(
+      {
+        'METRICS_COLLECTOR': {"min": 1}
+      }
+    )
+
+  def modifyHeapSizeProperties(self):
+    """
+    Modify the dictionary of heap size properties.
+    Must be overriden in child class.
+    """
+    # Nothing to do
+    pass
+
+  def modifyNotValuableComponents(self):
+    """
+    Modify the set of components whose host assignment is based on other 
services.
+    Must be overriden in child class.
+    """
+    self.notValuableComponents |= set(['METRICS_MONITOR'])
+
+  def modifyComponentsNotPreferableOnServer(self):
+    """
+    Modify the set of components that are not preferable on the server.
+    Must be overriden in child class.
+    """
+    # Nothing to do
+    pass
+
   def getServiceConfigurationRecommenderDict(self):
     parentRecommendConfDict = super(HDP22StackAdvisor, 
self).getServiceConfigurationRecommenderDict()
     childRecommendConfDict = {
@@ -1674,21 +1728,6 @@ class HDP22StackAdvisor(HDP21StackAdvisor):
                               "item": self.getWarnItem("Ranger Storm plugin 
should not be enabled in non-kerberos environment.")})
     return self.toConfigurationValidationProblems(validationItems, 
"ranger-env")
 
-  def getMastersWithMultipleInstances(self):
-    result = super(HDP22StackAdvisor, self).getMastersWithMultipleInstances()
-    result.extend(['METRICS_COLLECTOR'])
-    return result
-
-  def getNotValuableComponents(self):
-    result = super(HDP22StackAdvisor, self).getNotValuableComponents()
-    result.extend(['METRICS_MONITOR'])
-    return result
-
-  def getCardinalitiesDict(self):
-    result = super(HDP22StackAdvisor, self).getCardinalitiesDict()
-    result['METRICS_COLLECTOR'] = {"min": 1}
-    return result
-
   def getAffectedConfigs(self, services):
     affectedConfigs = super(HDP22StackAdvisor, 
self).getAffectedConfigs(services)
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/8258cf89/ambari-server/src/main/resources/stacks/HDPWIN/2.1/services/stack_advisor.py
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/resources/stacks/HDPWIN/2.1/services/stack_advisor.py 
b/ambari-server/src/main/resources/stacks/HDPWIN/2.1/services/stack_advisor.py
index cf52ade..4e4ef51 100644
--- 
a/ambari-server/src/main/resources/stacks/HDPWIN/2.1/services/stack_advisor.py
+++ 
b/ambari-server/src/main/resources/stacks/HDPWIN/2.1/services/stack_advisor.py
@@ -17,15 +17,68 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 """
 
+# Python Imports
 import re
 import sys
 import os
 from math import ceil
 
+# Local Imports
+from resource_management.core.logger import Logger
 from stack_advisor import DefaultStackAdvisor
 
 class HDPWIN21StackAdvisor(DefaultStackAdvisor):
 
+  def __init__(self):
+    super(HDPWIN21StackAdvisor, self).__init__()
+
+    self.modifyMastersWithMultipleInstances()
+    self.modifyCardinalitiesDict()
+    self.modifyHeapSizeProperties()
+    self.modifyNotValuableComponents()
+    self.modifyComponentsNotPreferableOnServer()
+
+  def modifyMastersWithMultipleInstances(self):
+    """
+    Modify the set of masters with multiple instances.
+    Must be overriden in child class.
+    """
+    self.mastersWithMultipleInstances |= set(['ZOOKEEPER_SERVER', 
'HBASE_MASTER'])
+
+  def modifyCardinalitiesDict(self):
+    """
+    Modify the dictionary of cardinalities.
+    Must be overriden in child class.
+    """
+    self.cardinalitiesDict.update(
+      {
+        'ZOOKEEPER_SERVER': {"min": 3},
+        'HBASE_MASTER': {"min": 1},
+      }
+    )
+
+  def modifyHeapSizeProperties(self):
+    """
+    Modify the dictionary of heap size properties.
+    Must be overriden in child class.
+    """
+    # Nothing to do
+    pass
+
+  def modifyNotValuableComponents(self):
+    """
+    Modify the set of components whose host assignment is based on other 
services.
+    Must be overriden in child class.
+    """
+    self.notValuableComponents |= set(['JOURNALNODE', 'ZKFC', 
'APP_TIMELINE_SERVER'])
+
+  def modifyComponentsNotPreferableOnServer(self):
+    """
+    Modify the set of components that are not preferable on the server.
+    Must be overriden in child class.
+    """
+    self.notPreferableOnServerComponents |= set(['STORM_UI_SERVER', 
'DRPC_SERVER', 'STORM_REST_API', 'NIMBUS'])
+
   def getComponentLayoutValidations(self, services, hosts):
     """Returns array of Validation objects about issues with hostnames 
components assigned to"""
     items = []
@@ -508,21 +561,8 @@ class HDPWIN21StackAdvisor(DefaultStackAdvisor):
                         {"config-name": 'hbase_master_heapsize', "item": 
self.validatorLessThenDefaultValue(properties, recommendedDefaults, 
'hbase_master_heapsize')}]
     return self.toConfigurationValidationProblems(validationItems, "hbase-env")
 
-  def getMastersWithMultipleInstances(self):
-    return ['ZOOKEEPER_SERVER', 'HBASE_MASTER']
-
-  def getNotValuableComponents(self):
-    return ['JOURNALNODE', 'ZKFC', 'APP_TIMELINE_SERVER']
-
-  def getNotPreferableOnServerComponents(self):
-    return ['STORM_UI_SERVER', 'DRPC_SERVER', 'STORM_REST_API', 'NIMBUS']
-
-  def getCardinalitiesDict(self):
-    return {
-      'ZOOKEEPER_SERVER': {"min": 3},
-      'HBASE_MASTER': {"min": 1},
-      }
 
+  # TODO, move to Service Advisors.
   def getComponentLayoutSchemes(self):
     return {
       'NAMENODE': {"else": 0},

http://git-wip-us.apache.org/repos/asf/ambari/blob/8258cf89/ambari-server/src/main/resources/stacks/HDPWIN/2.2/services/stack_advisor.py
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/resources/stacks/HDPWIN/2.2/services/stack_advisor.py 
b/ambari-server/src/main/resources/stacks/HDPWIN/2.2/services/stack_advisor.py
index d560146..26292d9 100644
--- 
a/ambari-server/src/main/resources/stacks/HDPWIN/2.2/services/stack_advisor.py
+++ 
b/ambari-server/src/main/resources/stacks/HDPWIN/2.2/services/stack_advisor.py
@@ -17,12 +17,16 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 """
 
+# Python Imports
 import math
 import os
 import re
 import sys
 from urlparse import urlparse
 
+# Local Imports
+from resource_management.core.logger import Logger
+
 def getSiteProperties(configurations, siteName):
   siteConfig = configurations.get(siteName)
   if siteConfig is None:
@@ -52,6 +56,56 @@ def isSecurePort(port):
 
 class HDPWIN22StackAdvisor(HDPWIN21StackAdvisor):
 
+  def __init__(self):
+    super(HDPWIN22StackAdvisor, self).__init__()
+    Logger.initialize_logger()
+
+    self.modifyMastersWithMultipleInstances()
+    self.modifyCardinalitiesDict()
+    self.modifyHeapSizeProperties()
+    self.modifyNotValuableComponents()
+    self.modifyComponentsNotPreferableOnServer()
+
+  def modifyMastersWithMultipleInstances(self):
+    """
+    Modify the set of masters with multiple instances.
+    Must be overriden in child class.
+    """
+    self.mastersWithMultipleInstances |= set(['METRICS_COLLECTOR'])
+
+  def modifyCardinalitiesDict(self):
+    """
+    Modify the dictionary of cardinalities.
+    Must be overriden in child class.
+    """
+    self.cardinalitiesDict.update(
+      {
+        'METRICS_COLLECTOR': {"min": 1}
+      }
+    )
+
+  def modifyHeapSizeProperties(self):
+    """
+    Modify the dictionary of heap size properties.
+    Must be overriden in child class.
+    """
+    # Nothing to do
+    pass
+
+  def modifyNotValuableComponents(self):
+    """
+    Modify the set of components whose host assignment is based on other 
services.
+    Must be overriden in child class.
+    """
+    self.notValuableComponents |= set(['METRICS_MONITOR'])
+
+  def modifyComponentsNotPreferableOnServer(self):
+    """
+    Modify the set of components that are not preferable on the server.
+    Must be overriden in child class.
+    """
+    self.notPreferableOnServerComponents |= set(['METRICS_COLLECTOR'])
+
   def getServiceConfigurationRecommenderDict(self):
     parentRecommendConfDict = super(HDPWIN22StackAdvisor, 
self).getServiceConfigurationRecommenderDict()
     childRecommendConfDict = {
@@ -1077,26 +1131,6 @@ class HDPWIN22StackAdvisor(HDPWIN21StackAdvisor):
     }
     return driverDict.get(databaseType.upper())
 
-  def getMastersWithMultipleInstances(self):
-    result = super(HDPWIN22StackAdvisor, 
self).getMastersWithMultipleInstances()
-    result.extend(['METRICS_COLLECTOR'])
-    return result
-
-  def getNotValuableComponents(self):
-    result = super(HDPWIN22StackAdvisor, self).getNotValuableComponents()
-    result.extend(['METRICS_MONITOR'])
-    return result
-
-  def getNotPreferableOnServerComponents(self):
-    result = super(HDPWIN22StackAdvisor, 
self).getNotPreferableOnServerComponents()
-    result.extend(['METRICS_COLLECTOR'])
-    return result
-
-  def getCardinalitiesDict(self):
-    result = super(HDPWIN22StackAdvisor, self).getCardinalitiesDict()
-    result['METRICS_COLLECTOR'] = {"min": 1}
-    return result
-
   def getAffectedConfigs(self, services):
     affectedConfigs = super(HDPWIN22StackAdvisor, 
self).getAffectedConfigs(services)
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/8258cf89/ambari-server/src/main/resources/stacks/service_advisor.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/service_advisor.py 
b/ambari-server/src/main/resources/stacks/service_advisor.py
index 7a3c7e9..4b32926 100644
--- a/ambari-server/src/main/resources/stacks/service_advisor.py
+++ b/ambari-server/src/main/resources/stacks/service_advisor.py
@@ -53,43 +53,50 @@ class ServiceAdvisor(DefaultStackAdvisor):
   Abstract class implemented by all service advisors.
   """
 
-  """
-  If any components of the service should be colocated with other services,
-  this is where you should set up that layout.  Example:
-
-    # colocate HAWQSEGMENT with DATANODE, if no hosts have been allocated for 
HAWQSEGMENT
-    hawqSegment = [component for component in serviceComponents if 
component["StackServiceComponents"]["component_name"] == "HAWQSEGMENT"][0]
-    if not self.isComponentHostsPopulated(hawqSegment):
-      for hostName in hostsComponentsMap.keys():
-        hostComponents = hostsComponentsMap[hostName]
-        if {"name": "DATANODE"} in hostComponents and {"name": "HAWQSEGMENT"} 
not in hostComponents:
-          hostsComponentsMap[hostName].append( { "name": "HAWQSEGMENT" } )
-        if {"name": "DATANODE"} not in hostComponents and {"name": 
"HAWQSEGMENT"} in hostComponents:
-          hostComponents.remove({"name": "HAWQSEGMENT"})
-  """
+
   def colocateService(self, hostsComponentsMap, serviceComponents):
+    """
+    Populate hostsComponentsMap with key = hostname and value = [{"name": 
"COMP_NAME_1"}, {"name": "COMP_NAME_2"}, ...]
+    of services that must be co-hosted and on which host they should be 
present.
+    :param hostsComponentsMap: Map from hostname to list of [{"name": 
"COMP_NAME_1"}, {"name": "COMP_NAME_2"}, ...]
+    present on on that host.
+    :param serviceComponents: Mapping of components
+
+    If any components of the service should be colocated with other services,
+    this is where you should set up that layout.  Example:
+
+      # colocate HAWQSEGMENT with DATANODE, if no hosts have been allocated 
for HAWQSEGMENT
+      hawqSegment = [component for component in serviceComponents if 
component["StackServiceComponents"]["component_name"] == "HAWQSEGMENT"][0]
+      if not self.isComponentHostsPopulated(hawqSegment):
+        for hostName in hostsComponentsMap.keys():
+          hostComponents = hostsComponentsMap[hostName]
+          if {"name": "DATANODE"} in hostComponents and {"name": 
"HAWQSEGMENT"} not in hostComponents:
+            hostsComponentsMap[hostName].append( { "name": "HAWQSEGMENT" } )
+          if {"name": "DATANODE"} not in hostComponents and {"name": 
"HAWQSEGMENT"} in hostComponents:
+            hostComponents.remove({"name": "HAWQSEGMENT"})
+    """
     pass
 
-  """
-  Any configuration recommendations for the service should be defined in this 
function.
-  This should be similar to any of the recommendXXXXConfigurations functions 
in the stack_advisor.py
-  such as recommendYARNConfigurations().
-  """
   def getServiceConfigurationRecommendations(self, configurations, 
clusterSummary, services, hosts):
+    """
+    Any configuration recommendations for the service should be defined in 
this function.
+    This should be similar to any of the recommendXXXXConfigurations functions 
in the stack_advisor.py
+    such as recommendYARNConfigurations().
+    """
     pass
 
-  """
-  Returns an array of Validation objects about issues with the hostnames to 
which components are assigned.
-  This should detect validation issues which are different than those the 
stack_advisor.py detects.
-  The default validations are in stack_advisor.py 
getComponentLayoutValidations function.
-  """
   def getServiceComponentLayoutValidations(self, services, hosts):
+    """
+    Returns an array of Validation objects about issues with the hostnames to 
which components are assigned.
+    This should detect validation issues which are different than those the 
stack_advisor.py detects.
+    The default validations are in stack_advisor.py 
getComponentLayoutValidations function.
+    """
     return []
 
-  """
-  Any configuration validations for the service should be defined in this 
function.
-  This should be similar to any of the validateXXXXConfigurations functions in 
the stack_advisor.py
-  such as validateHDFSConfigurations.
-  """
   def getServiceConfigurationsValidationItems(self, configurations, 
recommendedDefaults, services, hosts):
+    """
+    Any configuration validations for the service should be defined in this 
function.
+    This should be similar to any of the validateXXXXConfigurations functions 
in the stack_advisor.py
+    such as validateHDFSConfigurations.
+    """
     return []

http://git-wip-us.apache.org/repos/asf/ambari/blob/8258cf89/ambari-server/src/main/resources/stacks/stack_advisor.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/stacks/stack_advisor.py 
b/ambari-server/src/main/resources/stacks/stack_advisor.py
index 8865b70..d648a53 100644
--- a/ambari-server/src/main/resources/stacks/stack_advisor.py
+++ b/ambari-server/src/main/resources/stacks/stack_advisor.py
@@ -17,12 +17,18 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 """
 
+# Python Imports
 import imp
 import os
 import re
 import socket
 import traceback
 
+# Local imports
+from resource_management.core.exceptions import Fail
+from resource_management.core.logger import Logger
+
+
 class StackAdvisor(object):
   """
   Abstract class implemented by all stack advisors. Stack advisors advise on 
stack specific questions. 
@@ -310,6 +316,9 @@ class DefaultStackAdvisor(StackAdvisor):
 
   def __init__(self):
     self.services = None
+    
+    Logger.initialize_logger()
+    
     # Dictionary that maps serviceName or componentName to serviceAdvisor
     self.serviceAdvisorsDict = {}
 
@@ -318,6 +327,169 @@ class DefaultStackAdvisor(StackAdvisor):
     self.allRequestedProperties = None
 
 
+    # Data structures that may be extended by Service Advisors with 
information specific to each Service 
+    self.mastersWithMultipleInstances = set()
+    self.notValuableComponents = set()
+    self.notPreferableOnServerComponents = set()
+    self.cardinalitiesDict = {}
+    self.loaded_service_advisors = False
+
+
+  def getServiceComponentLayoutValidations(self, services, hosts):
+    """
+    Get a list of errors.
+
+    :param services: Dictionary of the form:
+    {
+      'changed-configurations': [],
+      'Versions": {
+        'parent_stack_version': '9.0',
+        'stack_name': 'HDP',
+        'stack_version': '9.0',
+        'stack_hierarchy': {
+          'stack_name': 'HDP',
+          'stack_versions': ['8.0', '7.0', ..., '1.0']
+        }
+      },
+      'ambari-server-properties': {'key': 'value', ...},
+      'services': [
+        {'StackServices': {
+          'advisor_path': 
'/var/lib/ambari-server/resources/common-services/MYSERVICE/1.2.3/service_advisor.py',
+          'service_version': '1.2.3',
+          'stack_name': 'HDP',
+          'service_name': 'MYSERVICE',
+          'stack_version': '9.0',
+          'advisor_name': 'MYSERVICEServiceAdvisor'
+        },
+        'components': [
+          {'StackServiceComponents': {
+            'stack_version': '9.0',
+            'decommission_allowed': True|False,
+            'display_name': 'My Service Display Name',
+            'stack_name': 'HDP',
+            'custom_commands': [],
+            'component_category': 'CLIENT|MASTER|SLAVE',
+            'advertise_version': True|False,
+            'is_client': True|False,
+            'is_master': False|False,
+            'bulk_commands_display_name': '',
+            'bulk_commands_master_component_name': '',
+            'service_name': 'MYSERVICE',
+            'has_bulk_commands_definition': True|False,
+            'reassign_allowed': True|False,
+            'recovery_enabled': True|False,
+            'cardinality': '0+|1|1+',
+            'hostnames': ['c6401.ambari.apache.org'],
+            'component_name': 'MY_COMPONENT_NAME'
+          },
+          'dependencies': []
+          },
+          ...
+          }],
+          'configurations': [
+            {
+              'StackConfigurations':
+              {
+                'stack_name': 'HDP',
+                'service_name': 'MYSERVICE',
+                'stack_version': '9.0',
+                'property_depends_on': [],
+                'type': 'myservice-config.xml',
+                'property_name': 'foo'
+              },
+              'dependencies': []
+            },
+            {
+              'StackConfigurations': {
+                'stack_name': 'HDP',
+                'service_name': 'ZOOKEEPER',
+                'stack_version':
+                '2.6',
+                'property_depends_on': [],
+                'type': 'zoo.cfg.xml',
+                'property_name': 'autopurge.snapRetainCount'
+              },
+              'dependencies': []
+            }
+            ...
+         ]
+        }
+      ],
+      'configurations': {}
+    }
+
+    :param hosts: Dictionary where hosts["items"] contains list of hosts on 
the cluster.
+    E.g. of the form,
+    {
+      'items': [
+        {
+          'Hosts':
+          {
+            'host_name': 'c6401.ambari.apache.org',
+            'public_host_name': 'c6401.ambari.apache.org',
+            'ip': '192.168.64.101',
+            'rack_info': '/default-rack',
+            'os_type': 'centos6',
+            'os_arch': 'x86_64',
+            'cpu_count': 1,
+            'ph_cpu_count': 1
+            'host_state': 'HEALTHY',
+            'total_mem': 2926196,
+            'host_status': 'HEALTHY',
+            'last_registration_time': 1481833146522,
+            'os_family': 'redhat6',
+            'last_heartbeat_time': 1481835051067,
+            'recovery_summary': 'DISABLED',
+            'host_health_report': '',
+            'desired_configs': None,
+            'disk_info': [
+              {
+                'available': '483608892',
+                'used': '3304964',
+                'percent': '1%',
+                'device': '/dev/mapper/VolGroup-lv_root',
+                'mountpoint': '/',
+                'type': 'ext4',
+                'size': '512971376'
+              },
+              ...
+            ],
+            'recovery_report': {
+              'component_reports': [],
+              'summary': 'DISABLED'
+            },
+            'last_agent_env': {
+              'transparentHugePage': 'always',
+              'hostHealth': {
+                'agentTimeStampAtReporting': 1481835031135,
+                'activeJavaProcs': [],
+                'serverTimeStampAtReporting': 1481835031180,
+                'liveServices': [{
+                  'status': 'Healthy',
+                  'name': 'ntpd',
+                  'desc': ''
+                }]
+              },
+              'umask': 18,
+              'reverseLookup': True,
+              'alternatives': [],
+              'existingUsers': [],
+              'firewallName': 'iptables',
+              'stackFoldersAndFiles': [],
+              'existingRepos': [],
+              'installedPackages': [],
+              'firewallRunning': False
+            }
+          }
+        }
+      ]
+    }
+
+    :return: List of errors
+    """
+    # To be overriden by subclass or Service Advisor
+    raise Fail("Must be overriden by subclass or Service Advisor")
+
   def getActiveHosts(self, hosts):
     """ Filters the list of specified hosts object and returns
         a list of hosts which are not in maintenance mode. """
@@ -328,37 +500,69 @@ class DefaultStackAdvisor(StackAdvisor):
     return hostsList
 
   def getServiceAdvisor(self, key):
-    if len(self.serviceAdvisorsDict) == 0:
+    """
+    Get the class name for the Service Advisor with the given name if it 
exists, or None otherwise.
+    :param key: Service Name
+    :return: Class name if it exists, or None otherwise.
+    """
+    if not self.loaded_service_advisors:
       self.loadServiceAdvisors()
-    return self.serviceAdvisorsDict[key]
+
+    return self.serviceAdvisorsDict[key] if key in self.serviceAdvisorsDict 
else None
 
   def loadServiceAdvisors(self):
+    """
+    If not loaded, for all of the services requested load the Service Advisor 
into the in-memory dictionary.
+    """
+    self.loaded_service_advisors = True
+    if self.services is None or "services" not in self.services:
+      return
+
     for service in self.services["services"]:
       serviceName = service["StackServices"]["service_name"]
-      self.serviceAdvisorsDict[serviceName] = 
self.instantiateServiceAdvisor(service)
+      serviceAdvisor = self.instantiateServiceAdvisor(service)
+      # This may store None for that service advisor.
+      self.serviceAdvisorsDict[serviceName] = serviceAdvisor
       for component in service["components"]:
         componentName = self.getComponentName(component)
         self.serviceAdvisorsDict[componentName] = 
self.serviceAdvisorsDict[serviceName]
 
   def instantiateServiceAdvisor(self, service):
-
-    serviceName = service["StackServices"]["service_name"]
-    className = service["StackServices"]["advisor_name"] if "advisor_name" in 
service["StackServices"] else None
+    """
+    Load the Service Advisor for the given services by finding the best class 
in the given file.
+    :param service: Service object that contains a path to the advisor being 
requested.
+    :return: The class name for the Service Advisor requested, or None if one 
could not be found.
+    """
+    service_name = service["StackServices"]["service_name"]
+    class_name = service["StackServices"]["advisor_name"] if "advisor_name" in 
service["StackServices"] else None
     path = service["StackServices"]["advisor_path"] if "advisor_path" in 
service["StackServices"] else None
 
-    if path is not None and os.path.exists(path) and className is not None:
+    class_name_pattern = re.compile("%s.*?ServiceAdvisor" % service_name, 
re.IGNORECASE)
+
+    if path is not None and os.path.exists(path) and class_name is not None:
       try:
         with open(path, 'rb') as fp:
-          serviceAdvisor = imp.load_module('service_advisor_impl', fp, path, 
('.py', 'rb', imp.PY_SOURCE))
-        if hasattr(serviceAdvisor, className):
-          print "ServiceAdvisor implementation for service {0} was 
loaded".format(serviceName)
-          return getattr(serviceAdvisor, className)()
-        else:
-          print "Failed to load or create ServiceAdvisor implementation for 
service {0}: " \
-                "Expecting class name {1} but it was not 
found.".format(serviceName, className)
+          service_advisor = imp.load_module('service_advisor_impl', fp, path, 
('.py', 'rb', imp.PY_SOURCE))
+
+          # Find the class name by reading from all of the available 
attributes of the python file.
+          attributes = dir(service_advisor)
+          best_class_name = class_name
+          for potential_class_name in attributes:
+            if not potential_class_name.startswith("__"):
+              m = class_name_pattern.match(potential_class_name)
+              if m:
+                best_class_name = potential_class_name
+                break
+
+          if hasattr(service_advisor, best_class_name):
+            Logger.info("ServiceAdvisor implementation for service {0} was 
loaded".format(service_name))
+            return getattr(service_advisor, best_class_name)()
+          else:
+            Logger.error("Failed to load or create ServiceAdvisor 
implementation for service {0}: " \
+                  "Expecting class name {1} but it was not 
found.".format(service_name, best_class_name))
       except Exception as e:
         traceback.print_exc()
-        print "Failed to load or create ServiceAdvisor implementation for 
service {0}".format(serviceName)
+        Logger.error("Failed to load or create ServiceAdvisor implementation 
for service {0}".format(service_name))
 
     return None
 
@@ -381,6 +585,124 @@ class DefaultStackAdvisor(StackAdvisor):
 
     return recommendations
 
+  def get_heap_size_properties(self, services):
+    """
+    Get dictionary of all of the components and a mapping to the heap-size 
configs, along with default values
+    if the heap-size config could not be found. This is used in calculations 
for the total memory needed to run
+    the cluster.
+    :param services: Dictionary that contains all of the services being 
requested. This is used to find heap-size
+    configs that have been delegated to Service Advisors to define.
+    :return: Dictionary of mappings from component name to another dictionary 
of the heap-size configs.
+    """
+    default = {
+      "NAMENODE":
+        [{"config-name": "hadoop-env",
+          "property": "namenode_heapsize",
+          "default": "1024m"}],
+      "SECONDARY_NAMENODE":
+        [{"config-name": "hadoop-env",
+          "property": "namenode_heapsize",
+          "default": "1024m"}],
+      "DATANODE":
+        [{"config-name": "hadoop-env",
+          "property": "dtnode_heapsize",
+          "default": "1024m"}],
+      "REGIONSERVER":
+        [{"config-name": "hbase-env",
+          "property": "hbase_regionserver_heapsize",
+          "default": "1024m"}],
+      "HBASE_MASTER":
+        [{"config-name": "hbase-env",
+          "property": "hbase_master_heapsize",
+          "default": "1024m"}],
+      "HIVE_CLIENT":
+        [{"config-name": "hive-env",
+          "property": "hive.client.heapsize",
+          "default": "1024m"}],
+      "HIVE_METASTORE":
+        [{"config-name": "hive-env",
+          "property": "hive.metastore.heapsize",
+          "default": "1024m"}],
+      "HIVE_SERVER":
+        [{"config-name": "hive-env",
+          "property": "hive.heapsize",
+          "default": "1024m"}],
+      "HISTORYSERVER":
+        [{"config-name": "mapred-env",
+          "property": "jobhistory_heapsize",
+          "default": "1024m"}],
+      "OOZIE_SERVER":
+        [{"config-name": "oozie-env",
+          "property": "oozie_heapsize",
+          "default": "1024m"}],
+      "RESOURCEMANAGER":
+        [{"config-name": "yarn-env",
+          "property": "resourcemanager_heapsize",
+          "default": "1024m"}],
+      "NODEMANAGER":
+        [{"config-name": "yarn-env",
+          "property": "nodemanager_heapsize",
+          "default": "1024m"}],
+      "APP_TIMELINE_SERVER":
+        [{"config-name": "yarn-env",
+          "property": "apptimelineserver_heapsize",
+          "default": "1024m"}],
+      "ZOOKEEPER_SERVER":
+        [{"config-name": "zookeeper-env",
+          "property": "zk_server_heapsize",
+          "default": "1024m"}],
+      "METRICS_COLLECTOR":
+        [{"config-name": "ams-hbase-env",
+          "property": "hbase_master_heapsize",
+          "default": "1024m"},
+         {"config-name": "ams-hbase-env",
+          "property": "hbase_regionserver_heapsize",
+          "default": "1024m"},
+         {"config-name": "ams-env",
+          "property": "metrics_collector_heapsize",
+          "default": "512m"}],
+      "ATLAS_SERVER":
+        [{"config-name": "atlas-env",
+          "property": "atlas_server_xmx",
+          "default": "2048m"}],
+      "LOGSEARCH_SERVER":
+        [{"config-name": "logsearch-env",
+          "property": "logsearch_app_max_memory",
+          "default": "1024m"}],
+      "LOGSEARCH_LOGFEEDER":
+        [{"config-name": "logfeeder-env",
+          "property": "logfeeder_max_mem",
+          "default": "512m"}],
+      "SPARK_JOBHISTORYSERVER":
+        [{"config-name": "spark-env",
+          "property": "spark_daemon_memory",
+          "default": "1024m"}],
+      "SPARK2_JOBHISTORYSERVER":
+        [{"config-name": "spark2-env",
+          "property": "spark_daemon_memory",
+          "default": "1024m"}]
+    }
+
+
+    try:
+      # Override any by reading from the Service Advisors
+
+      for service in services["services"]:
+        serviceName = service["StackServices"]["service_name"]
+        serviceAdvisor = self.getServiceAdvisor(serviceName)
+
+        # This seems confusing, but "self" may actually refer to the actual 
Service Advisor class that was loaded
+        # as opposed to this class.
+        advisor = serviceAdvisor if serviceAdvisor is not None else self
+
+        # TODO, switch this to a function instead of a property.
+        if hasattr(advisor, "heap_size_properties"):
+          # Override the values in "default" with those from the service 
advisor
+          default.update(advisor.heap_size_properties)
+    except Exception, e:
+      traceback.print_exc()
+    return default
+
   def createComponentLayoutRecommendations(self, services, hosts):
     self.services = services
 
@@ -683,6 +1005,7 @@ class DefaultStackAdvisor(StackAdvisor):
       serviceAdvisors = []
       for service in services["services"]:
         serviceName = service["StackServices"]["service_name"]
+        # "calculation" is a function pointer
         calculation = self.getServiceConfigurationRecommender(serviceName)
         if calculation is not None:
           calculation(configurations, cgClusterSummary, cgServices, cgHosts)
@@ -818,7 +1141,11 @@ class DefaultStackAdvisor(StackAdvisor):
 
   # Helper dictionaries
   def getComponentCardinality(self, componentName):
-    return self.getCardinalitiesDict().get(componentName, {"min": 1, "max": 1})
+    dict = self.getCardinalitiesDict()
+    if componentName in dict:
+      return dict[componentName]
+    else:
+      return {"min": 1, "max": 1}
 
   def getHostForComponent(self, component, hostsList):
     if len(hostsList) == 0:
@@ -844,16 +1171,16 @@ class DefaultStackAdvisor(StackAdvisor):
     return not (self.getComponentName(component) in 
self.getNotPreferableOnServerComponents() and self.isLocalHost(host))
 
   def getMastersWithMultipleInstances(self):
-    return []
+    return self.mastersWithMultipleInstances
 
   def getNotValuableComponents(self):
-    return []
+    return self.notValuableComponents
 
   def getNotPreferableOnServerComponents(self):
-    return []
+    return self.notPreferableOnServerComponents
 
   def getCardinalitiesDict(self):
-    return {}
+    return self.cardinalitiesDict
 
   def getComponentLayoutSchemes(self):
     """

http://git-wip-us.apache.org/repos/asf/ambari/blob/8258cf89/ambari-server/src/test/python/stacks/2.2/common/test_stack_advisor_perf.py
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/python/stacks/2.2/common/test_stack_advisor_perf.py 
b/ambari-server/src/test/python/stacks/2.2/common/test_stack_advisor_perf.py
index 5b8c30c..82cef1b 100644
--- a/ambari-server/src/test/python/stacks/2.2/common/test_stack_advisor_perf.py
+++ b/ambari-server/src/test/python/stacks/2.2/common/test_stack_advisor_perf.py
@@ -25,13 +25,15 @@ from mock.mock import patch
 
 class TestHDP22StackAdvisor(TestCase):
 
-  def instantiate_stack_advisor(self, testDirectory, base_stack_advisor_path):
+  def instantiate_stack_advisor(self, testDirectory):
+    default_stack_advisor_path = os.path.join(testDirectory, 
'../../../../../main/resources/stacks/stack_advisor.py')
     hdp_206_stack_advisor_path = os.path.join(testDirectory, 
'../../../../../main/resources/stacks/HDP/2.0.6/services/stack_advisor.py')
     hdp_21_stack_advisor_path = os.path.join(testDirectory, 
'../../../../../main/resources/stacks/HDP/2.1/services/stack_advisor.py')
     hdp_22_stack_advisor_path = os.path.join(testDirectory, 
'../../../../../main/resources/stacks/HDP/2.2/services/stack_advisor.py')
-    hdp_206_stack_advisor_classname = 'HDP22StackAdvisor'
-    with open(base_stack_advisor_path, 'rb') as fp:
-      imp.load_module('stack_advisor', fp, base_stack_advisor_path, ('.py', 
'rb', imp.PY_SOURCE))
+    hdp_206_stack_advisor_classname = 'HDP206StackAdvisor'
+
+    with open(default_stack_advisor_path, 'rb') as fp:
+      imp.load_module('stack_advisor', fp, default_stack_advisor_path, ('.py', 
'rb', imp.PY_SOURCE))
     with open(hdp_206_stack_advisor_path, 'rb') as fp:
       imp.load_module('stack_advisor_impl', fp, hdp_206_stack_advisor_path, 
('.py', 'rb', imp.PY_SOURCE))
     with open(hdp_21_stack_advisor_path, 'rb') as fp:
@@ -45,25 +47,19 @@ class TestHDP22StackAdvisor(TestCase):
   def test_performance(self, getfqdn_method):
     getfqdn_method.side_effect = lambda 
host='perf400-a-1.c.pramod-thangali.internal': host
     testDirectory = os.path.dirname(os.path.abspath(__file__))
-    old_stack_advisor_path = os.path.join(testDirectory, 
'../../../../../test/resources/stacks/old_stack_advisor.py')
     current_stack_advisor_path = os.path.join(testDirectory, 
'../../../../../main/resources/stacks/stack_advisor.py')
 
     for folder_name in ['1', '2']:
-      stack_advisor_old = self.instantiate_stack_advisor(testDirectory, 
old_stack_advisor_path)
       services = json.load(open(os.path.join(testDirectory, folder_name + 
'/services.json')))
       hosts = json.load(open(os.path.join(testDirectory, folder_name + 
'/hosts.json')))
-      start = time.time()
-      recommendation_old = 
stack_advisor_old.recommendComponentLayout(services, hosts)
-      time_taken = time.time() - start
-      print "time taken by old stack_advisor.py = " + str(time_taken)
 
-      stack_advisor = self.instantiate_stack_advisor(testDirectory, 
current_stack_advisor_path)
+      stack_advisor = self.instantiate_stack_advisor(testDirectory)
       start = time.time()
       recommendation = stack_advisor.recommendComponentLayout(services, hosts)
       time_taken = time.time() - start
       print "time taken by current stack_advisor.py = " + str(time_taken)
 
-      self.assertEquals(recommendation, recommendation_old,
-                        "current stack_advisor gives different results running 
on folder '" + folder_name + "'")
+      self.assertTrue(time_taken < 0.1)
+
 
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/8258cf89/ambari-server/src/test/python/stacks/2.3/common/test_stack_advisor.py
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/python/stacks/2.3/common/test_stack_advisor.py 
b/ambari-server/src/test/python/stacks/2.3/common/test_stack_advisor.py
index 378522e..c89eee4 100644
--- a/ambari-server/src/test/python/stacks/2.3/common/test_stack_advisor.py
+++ b/ambari-server/src/test/python/stacks/2.3/common/test_stack_advisor.py
@@ -21,13 +21,14 @@ import os
 import socket
 from unittest import TestCase
 from mock.mock import patch
-
+import unittest
 
 class TestHDP23StackAdvisor(TestCase):
 
   def setUp(self):
     import imp
     self.maxDiff = None
+    unittest.util._MAX_LENGTH=2000
     self.testDirectory = os.path.dirname(os.path.abspath(__file__))
     stackAdvisorPath = os.path.join(self.testDirectory, 
'../../../../../main/resources/stacks/stack_advisor.py')
     hdp206StackAdvisorPath = os.path.join(self.testDirectory, 
'../../../../../main/resources/stacks/HDP/2.0.6/services/stack_advisor.py')
@@ -1966,12 +1967,27 @@ class TestHDP23StackAdvisor(TestCase):
       "ramPerContainer": 256
     }
     expected = {
-      'logfeeder-env': {'property_attributes': 
{'logfeeder_external_solr_kerberos_keytab': {'visible': 'false'},
-                                                
'logfeeder_external_solr_kerberos_principal': {'visible': 'false'}}},
-      'logsearch-common-env': {'properties': 
{'logsearch_external_solr_kerberos_enabled': 'false'},
-                               'property_attributes': 
{'logsearch_external_solr_kerberos_enabled': {'visible': 'false'}}},
-      'logsearch-env': {'property_attributes': 
{'logsearch_external_solr_kerberos_keytab': {'visible': 'false'},
-                                                
'logsearch_external_solr_kerberos_principal': {'visible': 'false'}}},
+      'logfeeder-env': {
+        'property_attributes': {
+          'logfeeder_external_solr_kerberos_keytab': {'visible': 'false'
+          },
+          'logfeeder_external_solr_kerberos_principal': {'visible': 'false'}
+        }
+      },
+      'logsearch-common-env': {
+        'properties': {
+          'logsearch_external_solr_kerberos_enabled': 'false'
+        },
+        'property_attributes': {
+          'logsearch_external_solr_kerberos_enabled': {'visible': 'false'}
+        }
+      },
+      'logsearch-env': {
+        'property_attributes': {
+          'logsearch_external_solr_kerberos_keytab': {'visible': 'false'},
+          'logsearch_external_solr_kerberos_principal': {'visible': 'false'}
+        }
+      },
       'logsearch-properties': {
         'properties': {
           "logsearch.collection.service.logs.numshards" : "2",

Reply via email to