浏览代码

AMBARI-971. Add api support for creating multiple resources in a single request. (John Speidel via mahadev)

git-svn-id: https://svn.apache.org/repos/asf/incubator/ambari/branches/AMBARI-666@1406492 13f79535-47bb-0310-9956-ffa450edef68
Mahadev Konar 12 年之前
父节点
当前提交
29ded1af83
共有 50 个文件被更改,包括 700 次插入362 次删除
  1. 3 0
      AMBARI-666-CHANGES.txt
  2. 0 1
      ambari-agent/conf/unix/ambari-agent
  3. 2 2
      ambari-agent/conf/unix/ambari.ini
  4. 11 3
      ambari-agent/pom.xml
  5. 1 4
      ambari-agent/src/main/puppet/modules/hdp-hbase/manifests/init.pp
  6. 1 2
      ambari-agent/src/main/puppet/modules/hdp-repos/templates/repo.erb
  7. 5 1
      ambari-agent/src/main/puppet/modules/hdp/manifests/params.pp
  8. 4 0
      ambari-agent/src/main/python/ambari_agent/ActionQueue.py
  9. 79 0
      ambari-agent/src/main/python/ambari_agent/RepoInstaller.py
  10. 1 37
      ambari-agent/src/main/python/ambari_agent/manifestGenerator.py
  11. 68 35
      ambari-agent/src/main/python/ambari_agent/puppetExecutor.py
  12. 1 0
      ambari-server/conf/unix/ambari.properties
  13. 18 1
      ambari-server/pom.xml
  14. 6 7
      ambari-server/src/main/java/org/apache/ambari/server/api/handlers/BaseManagementHandler.java
  15. 25 17
      ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryImpl.java
  16. 4 22
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/BaseResourceDefinition.java
  17. 1 1
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java
  18. 10 17
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/ComponentResourceDefinition.java
  19. 8 9
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/ConfigurationResourceDefinition.java
  20. 7 14
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostComponentResourceDefinition.java
  21. 9 3
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostResourceDefinition.java
  22. 3 27
      ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceDefinition.java
  23. 3 2
      ambari-server/src/main/java/org/apache/ambari/server/api/services/BasePersistenceManager.java
  24. 19 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/ComponentService.java
  25. 9 8
      ambari-server/src/main/java/org/apache/ambari/server/api/services/ConfigurationService.java
  26. 17 8
      ambari-server/src/main/java/org/apache/ambari/server/api/services/CreatePersistenceManager.java
  27. 5 1
      ambari-server/src/main/java/org/apache/ambari/server/api/services/DeletePersistenceManager.java
  28. 19 0
      ambari-server/src/main/java/org/apache/ambari/server/api/services/HostComponentService.java
  29. 30 10
      ambari-server/src/main/java/org/apache/ambari/server/api/services/HostService.java
  30. 3 9
      ambari-server/src/main/java/org/apache/ambari/server/api/services/PersistKeyValueService.java
  31. 8 2
      ambari-server/src/main/java/org/apache/ambari/server/api/services/PersistenceManager.java
  32. 3 2
      ambari-server/src/main/java/org/apache/ambari/server/api/services/Request.java
  33. 1 1
      ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestImpl.java
  34. 6 2
      ambari-server/src/main/java/org/apache/ambari/server/api/services/UpdatePersistenceManager.java
  35. 20 11
      ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/JsonPropertyParser.java
  36. 3 2
      ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/RequestBodyParser.java
  37. 2 0
      ambari-server/src/main/python/ambari-server.py
  38. 26 17
      ambari-server/src/main/python/bootstrap.py
  39. 38 7
      ambari-server/src/main/python/setupAgent.py
  40. 1 1
      ambari-server/src/main/resources/ca.config
  41. 6 8
      ambari-server/src/test/java/org/apache/ambari/server/api/handlers/CreateHandlerTest.java
  42. 7 12
      ambari-server/src/test/java/org/apache/ambari/server/api/handlers/DeleteHandlerTest.java
  43. 7 12
      ambari-server/src/test/java/org/apache/ambari/server/api/handlers/UpdateHandlerTest.java
  44. 1 6
      ambari-server/src/test/java/org/apache/ambari/server/api/query/QueryImplTest.java
  45. 1 1
      ambari-server/src/test/java/org/apache/ambari/server/api/services/ConfigurationServiceTest.java
  46. 59 13
      ambari-server/src/test/java/org/apache/ambari/server/api/services/CreatePersistenceManagerTest.java
  47. 1 1
      ambari-server/src/test/java/org/apache/ambari/server/api/services/DeletePersistenceManagerTest.java
  48. 57 3
      ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java
  49. 15 12
      ambari-server/src/test/java/org/apache/ambari/server/api/services/UpdatePersistenceManagerTest.java
  50. 66 8
      ambari-server/src/test/java/org/apache/ambari/server/api/services/parsers/JsonPropertyParserTest.java

+ 3 - 0
AMBARI-666-CHANGES.txt

@@ -12,6 +12,9 @@ AMBARI-666 branch (unreleased changes)
 
 
   NEW FEATURES
   NEW FEATURES
 
 
+  AMBARI-971. Add api support for creating multiple resources in a single
+  request. (John Speidel via mahadev)
+
   AMBARI-970. Add additional Ganglia metrics and JMX properties. (Tom
   AMBARI-970. Add additional Ganglia metrics and JMX properties. (Tom
   Beerbower via mahadev)
   Beerbower via mahadev)
 
 

+ 0 - 1
ambari-agent/conf/unix/ambari-agent

@@ -6,7 +6,6 @@
 case "$1" in
 case "$1" in
   start)
   start)
         echo -e "Starting ambari-agent"
         echo -e "Starting ambari-agent"
-        export AMBARI_PASSPHRASE=pass_phrase
         python /usr/lib/python2.6/site-packages/ambari_agent/main.py
         python /usr/lib/python2.6/site-packages/ambari_agent/main.py
         ;;
         ;;
   stop)
   stop)

+ 2 - 2
ambari-agent/conf/unix/ambari.ini

@@ -10,7 +10,7 @@ prefix=/tmp/ambari-agent
 installprefix=/var/ambari/
 installprefix=/var/ambari/
 
 
 [puppet]
 [puppet]
-puppetmodules=/etc/ambari-agent/puppet
+puppetmodules=/var/lib/ambari-agent/puppet
 puppet_home=/usr/bin/puppet
 puppet_home=/usr/bin/puppet
 facter_home=/usr/bin/facter
 facter_home=/usr/bin/facter
 
 
@@ -19,7 +19,7 @@ maxretries=2
 sleepBetweenRetries=1
 sleepBetweenRetries=1
 
 
 [security]
 [security]
-keysdir=/etc/ambari-agent/keys
+keysdir=/var/lib/ambari-agent/keys
 server_crt=ca.crt
 server_crt=ca.crt
 passphrase_env_var_name=AMBARI_PASSPHRASE
 passphrase_env_var_name=AMBARI_PASSPHRASE
 
 

+ 11 - 3
ambari-agent/pom.xml

@@ -113,6 +113,9 @@
           <copyright>2012, Apache Software Foundation</copyright>
           <copyright>2012, Apache Software Foundation</copyright>
           <group>Development</group>
           <group>Development</group>
           <description>Maven Recipe: RPM Package.</description>
           <description>Maven Recipe: RPM Package.</description>
+          <requires>
+            <require>puppet = 2.7.9</require>
+          </requires>
           <mappings>
           <mappings>
 
 
             <mapping>
             <mapping>
@@ -125,7 +128,7 @@
             </mapping>
             </mapping>
 
 
             <mapping>
             <mapping>
-              <directory>/etc/${project.artifactId}/puppet</directory>
+              <directory>/var/lib/${project.artifactId}/puppet</directory>
               <sources>
               <sources>
                 <source>
                 <source>
                   <location>src/main/puppet</location>
                   <location>src/main/puppet</location>
@@ -155,9 +158,14 @@
             <mapping>
             <mapping>
               <directory>/var/run/ambari</directory>
               <directory>/var/run/ambari</directory>
             </mapping>
             </mapping>
-
             <mapping>
             <mapping>
-              <directory>/etc/ambari-agent/keys</directory>
+              <directory>/var/lib/${project.artifactId}/keys</directory>
+            </mapping>
+            <mapping>
+              <directory>/var/log/ambari</directory>
+            </mapping>
+            <mapping>
+              <directory>/var/ambari</directory>
             </mapping>
             </mapping>
 
 
             <!-- -->
             <!-- -->

+ 1 - 4
ambari-agent/src/main/puppet/modules/hdp-hbase/manifests/init.pp

@@ -40,9 +40,6 @@ class hdp-hbase(
       configuration => $configuration['hbase-site']
       configuration => $configuration['hbase-site']
       }
       }
     }
     }
-    hdp-hbase::configfile { 'regionservers':}
-    Anchor['hdp-hbase::begin'] -> Hdp::Package['hbase'] -> Hdp::User[$hbase_user] -> Hdp::Directory[$config_dir] -> 
-    Hdp-hbase::Configfile<||> ->  Anchor['hdp-hbase::end']
 
 
   if has_key($configuration, 'hbase-policy') {
   if has_key($configuration, 'hbase-policy') {
     configgenerator::configfile{'hbase-policy': 
     configgenerator::configfile{'hbase-policy': 
@@ -91,7 +88,7 @@ define hdp-hbase::configfile(
   $mode = undef,
   $mode = undef,
   $hbase_master_host = undef,
   $hbase_master_host = undef,
   $template_tag = undef,
   $template_tag = undef,
-  $type = undef,
+  $type = undef
 ) 
 ) 
 {
 {
   if ($name == 'hadoop-metrics.properties') {
   if ($name == 'hadoop-metrics.properties') {

+ 1 - 2
ambari-agent/src/main/puppet/modules/hdp-repos/templates/repo.erb

@@ -22,6 +22,5 @@
 [<%=repo_id%>]
 [<%=repo_id%>]
 name=<%=repo_name %>
 name=<%=repo_name %>
 baseurl=<%=base_url %>
 baseurl=<%=base_url %>
+path=/
 enabled=1
 enabled=1
-
-

+ 5 - 1
ambari-agent/src/main/puppet/modules/hdp/manifests/params.pp

@@ -67,6 +67,9 @@ class hdp::params()
         /^6\..+$/: { $hdp_os_type = "rhel6" }
         /^6\..+$/: { $hdp_os_type = "rhel6" }
       }
       }
     }
     }
+    suse: {
+      $hdp_os_type = "suse"
+    }
     default: {
     default: {
       hdp_fail("No support for os  ${hdp_os} ${hdp_os_version}")
       hdp_fail("No support for os  ${hdp_os} ${hdp_os_version}")
     }
     }
@@ -354,7 +357,8 @@ class hdp::params()
 
 
   $repos_paths = 
   $repos_paths = 
   {
   {
-    centos6 => '/etc/yum.repos.d'
+    centos6 => '/etc/yum.repos.d',
+    suse => '/etc/zypp/repos.d'
   }
   }
 
 
   }
   }

+ 4 - 0
ambari-agent/src/main/python/ambari_agent/ActionQueue.py

@@ -133,6 +133,10 @@ class ActionQueue(threading.Thread):
                   'exitCode' : commandresult['exitcode'],
                   'exitCode' : commandresult['exitcode'],
                   'serviceName' : serviceName,
                   'serviceName' : serviceName,
                   'status' : status}
                   'status' : status}
+    if roleResult['stdout'] == '':
+      roleResult['stdout'] = 'None'
+    if roleResult['stderr'] == '':
+      roleResult['stderr'] = 'None'
     result.append(roleResult)
     result.append(roleResult)
     pass
     pass
     return result
     return result

+ 79 - 0
ambari-agent/src/main/python/ambari_agent/RepoInstaller.py

@@ -0,0 +1,79 @@
+#!/usr/bin/env python2.6
+
+'''
+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.
+'''
+
+import logging
+import os
+import json
+from shell import shellRunner
+from manifestGenerator import writeImports
+
+
+PUPPET_EXT=".pp"
+
+logger = logging.getLogger()
+
+class RepoInstaller:
+  def __init__(self, parsedJson, path, modulesdir, taskId):
+    self.parsedJson = parsedJson
+    self.path = path
+    self.modulesdir = modulesdir
+    self.taskId = taskId
+    self.sh = shellRunner()
+
+  def prepareReposInfo(self):
+    params = {}
+    self.repoInfoList = []
+    if self.parsedJson.has_key('hostLevelParams'):
+      params = self.parsedJson['hostLevelParams']
+    if params.has_key('repo_info'):
+      self.repoInfoList = params['repo_info']
+
+  def generateFiles(self):
+    repoPuppetFiles = []
+    for repo in self.repoInfoList:
+      repoFile = open(self.path + os.sep + repo['repo_id'] + '-' + str(self.taskId) + PUPPET_EXT, 'w+')
+      writeImports(repoFile, self.modulesdir, inputFileName='imports.txt')
+      repoFile.write('node /default/ {')
+      repoFile.write('class{ "hdp-repos::process_repo" : ' + ' os_type => "' + repo['os_type'] +
+      '", repo_id => "' + repo['repo_id'] + '", base_url => "' + repo['base_url'] +
+      '", repo_name => "' + repo['repo_name'] + '" }' )
+      repoFile.write('}')
+      repoFile.close()
+      repoPuppetFiles.append(repoFile.name)
+
+    return repoPuppetFiles
+
+  def installRepos(self):
+    self.prepareReposInfo()
+    repoPuppetFiles = self.generateFiles()
+    return repoPuppetFiles
+
+def main():
+  #Test code
+  jsonFile = open('test.json', 'r')
+  jsonStr = jsonFile.read() 
+  parsedJson = json.loads(jsonStr)
+  repoInstaller = RepoInstaller(parsedJson, '/tmp', '/home/centos/ambari_repo_info/ambari-agent/src/main/puppet/modules')
+  repoInstaller.installRepos()
+  
+if __name__ == '__main__':
+  main()
+
+

+ 1 - 37
ambari-agent/src/main/python/ambari_agent/manifestGenerator.py

@@ -24,8 +24,6 @@ import logging
 from uuid import getnode as get_mac
 from uuid import getnode as get_mac
 from shell import shellRunner
 from shell import shellRunner
 
 
-REPO_INFO_DIR="repos_info"
-PUPPET_EXT=".pp"
 
 
 logger = logging.getLogger()
 logger = logging.getLogger()
 
 
@@ -138,9 +136,7 @@ def writeNodes(outputFile, clusterHostInfo):
 def writeParams(outputFile, params, modulesdir):
 def writeParams(outputFile, params, modulesdir):
 
 
   for paramName in params.iterkeys():
   for paramName in params.iterkeys():
-    # todo handle repo information properly
-    if paramName == 'repo_info':
-      processRepo(params[paramName],modulesdir)      
+    if paramName == 'repo_info':     
       continue
       continue
       
       
 
 
@@ -257,39 +253,7 @@ def writeStages(outputFile, numStages):
   
   
   outputFile.write('\n')
   outputFile.write('\n')
 
 
-def processRepo(repoInfoList, modulesdir):
 
 
-  if not os.path.exists(REPO_INFO_DIR):
-    os.makedirs(REPO_INFO_DIR)
-
-  for repo in repoInfoList:
-
-    repoFile = open(REPO_INFO_DIR + os.sep + repo['repo_id'] + PUPPET_EXT, 'w+')
-    writeImports(repoFile, modulesdir, inputFileName='imports.txt')
-    repoFile.write('node /default/ {')
-    repoFile.write('class{ "hdp-repos::process_repo" : ' + ' os_type => "' + repo['os_type'] +
-    '", repo_id => "' + repo['repo_id'] + '", base_url => "' + repo['base_url'] +
-    '", repo_name => "' + repo['repo_name'] + '" }' )
-    repoFile.write('}')
-    repoFile.close()
-
-
-def installRepos():
-  sh = shellRunner()
-  agentdir = os.getcwd()
-  confdir = os.path.abspath(agentdir + ".." + os.sep + ".." + 
-                               os.sep + ".." + os.sep + "puppet")
-
-  for repo in os.listdir(REPO_INFO_DIR):
-    if not repo.endswith(PUPPET_EXT):
-      continue
-    logfile = repo + '_log.log'
-    res = sh.run(['puppet apply', '--confdir=' + confdir, REPO_INFO_DIR + os.sep + repo,
-    '--logdest=' + agentdir + os.sep + REPO_INFO_DIR + os.sep + logfile])
-    if res['exitCode'] == 0:
-      logger.info('Repository ' + repo + ' was installed')
-    else:
-      logger.error('Repository ' + repo + ' wasn''t installed. Please find detailed info in logfile:' + logfile)
   
   
 def main():
 def main():
   logging.basicConfig(level=logging.DEBUG)    
   logging.basicConfig(level=logging.DEBUG)    

+ 68 - 35
ambari-agent/src/main/python/ambari_agent/puppetExecutor.py

@@ -22,6 +22,7 @@ import os.path
 import logging
 import logging
 import subprocess
 import subprocess
 from manifestGenerator import generateManifest
 from manifestGenerator import generateManifest
+from RepoInstaller import RepoInstaller
 import pprint
 import pprint
 from Grep import Grep
 from Grep import Grep
 
 
@@ -29,6 +30,9 @@ logger = logging.getLogger()
 
 
 class puppetExecutor:
 class puppetExecutor:
 
 
+  """ Class that executes the commands that come from the server using puppet.
+  This is the class that provides the pluggable point for executing the puppet"""
+
   # How many lines from command output send to server
   # How many lines from command output send to server
   OUTPUT_LAST_LINES = 10
   OUTPUT_LAST_LINES = 10
   # How many lines from command error output send to server (before Err phrase)
   # How many lines from command error output send to server (before Err phrase)
@@ -38,9 +42,6 @@ class puppetExecutor:
 
 
   NO_ERROR = "none"
   NO_ERROR = "none"
 
 
-  """ Class that executes the commands that come from the server using puppet.
-  This is the class that provides the pluggable point for executing the puppet"""
-  
   def __init__(self, puppetModule, puppetInstall, facterInstall, tmpDir):
   def __init__(self, puppetModule, puppetInstall, facterInstall, tmpDir):
     self.puppetModule = puppetModule
     self.puppetModule = puppetModule
     self.puppetInstall = puppetInstall
     self.puppetInstall = puppetInstall
@@ -76,39 +77,70 @@ class puppetExecutor:
       taskId = command['taskId']
       taskId = command['taskId']
       
       
     puppetEnv = os.environ
     puppetEnv = os.environ
+    #Install repos
+    modulesdir = self.puppetModule + "/modules"
+    repoInstaller = RepoInstaller(command, self.tmpDir, modulesdir, taskId)
+
+    puppetFiles = repoInstaller.installRepos()
     siteppFileName = os.path.join(self.tmpDir, "site-" + str(taskId) + ".pp") 
     siteppFileName = os.path.join(self.tmpDir, "site-" + str(taskId) + ".pp") 
-    generateManifest(command, siteppFileName, self.puppetModule + "/modules")
-    puppetcommand = self.puppetCommand(siteppFileName)
-    """ Run the command and make sure the output gets propagated"""
-    rubyLib = ""
-    if os.environ.has_key("RUBYLIB"):
-      rubyLib = os.environ["RUBYLIB"]
-      logger.info("Ruby Lib env from Env " + rubyLib)
-    rubyLib = rubyLib + ":" + self.facterLib() + ":" + self.puppetLib()
-    puppetEnv["RUBYLIB"] = rubyLib
-    logger.info("Setting RUBYLIB as: " + rubyLib)
-    logger.info("Running command " + pprint.pformat(puppetcommand))
-    puppet = subprocess.Popen(puppetcommand,
-                                  stdout=subprocess.PIPE,
-                                  stderr=subprocess.PIPE,
-                                  env=puppetEnv)
-    stderr_out = puppet.communicate()
-    error = self.NO_ERROR
-    returncode = 0
-    if puppet.returncode != 0 and puppet.returncode != 2:
-      returncode = puppet.returncode
-      error = stderr_out[1]
-      logging.error("Error running puppet: \n" + stderr_out[1])
-      pass
-    result["stderr"] = error
-    puppetOutput = stderr_out[0]
-    logger.info("Output from puppet :\n" + puppetOutput)
-    result["exitcode"] = returncode
+    puppetFiles.append(siteppFileName)
+    generateManifest(command, siteppFileName, modulesdir)
+    #Run all puppet commands, from manifest generator and for repos installation
+    #Appending outputs and errors, exitcode - maximal from all
+    for puppetFile in puppetFiles:
+      puppetcommand = self.puppetCommand(puppetFile)
+      """ Run the command and make sure the output gets propagated"""
+      rubyLib = ""
+      if os.environ.has_key("RUBYLIB"):
+        rubyLib = os.environ["RUBYLIB"]
+        logger.info("Ruby Lib env from Env " + rubyLib)
+      rubyLib = rubyLib + ":" + self.facterLib() + ":" + self.puppetLib()
+      puppetEnv["RUBYLIB"] = rubyLib
+      logger.info("Setting RUBYLIB as: " + rubyLib)
+      logger.info("Running command " + pprint.pformat(puppetcommand))
+      puppet = subprocess.Popen(puppetcommand,
+                                    stdout=subprocess.PIPE,
+                                    stderr=subprocess.PIPE,
+                                    env=puppetEnv)
+      stderr_out = puppet.communicate()
+      error = "none"
+      returncode = 0
+      if (puppet.returncode != 0 and puppet.returncode != 2) :
+        returncode = puppet.returncode
+        error = stderr_out[1]
+        logging.error("Error running puppet: \n" + stderr_out[1])
+        pass
+		
+      if result.has_key("stderr"):
+        result["stderr"] = result["stderr"] + os.linesep + error
+      else:
+        result["stderr"] = error
+      puppetOutput = stderr_out[0]
+      logger.info("Output from puppet :\n" + puppetOutput)
+      if result.has_key("exitcode"):
+        result["exitcode"] = max(returncode, result["exitcode"])
+      else:
+        result["exitcode"] = returncode
+        
+
+      if result.has_key("stdout"):
+        result["stdout"] = result["stdout"] + os.linesep + puppetOutput
+      else:
+        result["stdout"] = puppetOutput
+
     if error == self.NO_ERROR:
     if error == self.NO_ERROR:
-      result["stdout"] = grep.tail(puppetOutput, self.OUTPUT_LAST_LINES)
+      if result.has_key("stdout"):
+        result["stdout"] = result["stdout"] + os.linesep + str(grep.tail(puppetOutput, self.OUTPUT_LAST_LINES))
+      else:
+        result["stdout"] = grep.tail(puppetOutput, self.OUTPUT_LAST_LINES)
     else:
     else:
-      result["stdout"] = grep.grep(puppetOutput, "err", self.ERROR_LAST_LINES_BEFORE, self.ERROR_LAST_LINES_AFTER)
+      if result.has_key("stdout"):
+        result["stdout"] = result["stdout"] + os.linesep + str(grep.grep(puppetOutput, "err", self.ERROR_LAST_LINES_BEFORE, self.ERROR_LAST_LINES_AFTER))
+      else:
+        result["stdout"] = str(grep.grep(puppetOutput, "err", self.ERROR_LAST_LINES_BEFORE, self.ERROR_LAST_LINES_AFTER))
+	
     logger.info("ExitCode : "  + str(result["exitcode"]))
     logger.info("ExitCode : "  + str(result["exitcode"]))
+
     return result
     return result
  
  
 def main():
 def main():
@@ -118,14 +150,15 @@ def main():
   jsonStr = jsonFile.read() 
   jsonStr = jsonFile.read() 
   # Below is for testing only.
   # Below is for testing only.
   
   
-  puppetInstance = puppetExecutor("/root/workspace/ambari-workspace/ambari-git/ambari-agent/src/main/puppet/",
-                                  "/root/workspace/puppet-install/puppet-2.7.9",
+  puppetInstance = puppetExecutor("/home/centos/ambari_repo_info/ambari-agent/src/main/puppet/",
+                                  "/usr/",
                                   "/root/workspace/puppet-install/facter-1.6.10/",
                                   "/root/workspace/puppet-install/facter-1.6.10/",
                                   "/tmp")
                                   "/tmp")
   jsonFile = open('test.json', 'r')
   jsonFile = open('test.json', 'r')
   jsonStr = jsonFile.read() 
   jsonStr = jsonFile.read() 
   parsedJson = json.loads(jsonStr)
   parsedJson = json.loads(jsonStr)
-  puppetInstance.runCommand(parsedJson)
+  result = puppetInstance.runCommand(parsedJson)
+  logger.debug(result)
   
   
 if __name__ == '__main__':
 if __name__ == '__main__':
   main()
   main()

+ 1 - 0
ambari-server/conf/unix/ambari.properties

@@ -1,3 +1,4 @@
 security.server.keys_dir = /var/lib/ambari-server/keys
 security.server.keys_dir = /var/lib/ambari-server/keys
 resources.dir = /var/lib/ambari-server/resources
 resources.dir = /var/lib/ambari-server/resources
 jdk.url=http://public-repo-1.hortonworks.com/ARTIFACTS/jdk-6u31-linux-x64.bin
 jdk.url=http://public-repo-1.hortonworks.com/ARTIFACTS/jdk-6u31-linux-x64.bin
+metadata.path=/var/lib/ambari-server/resources/stacks

+ 18 - 1
ambari-server/pom.xml

@@ -22,7 +22,7 @@
   <artifactId>ambari-server</artifactId>
   <artifactId>ambari-server</artifactId>
   <packaging>jar</packaging>
   <packaging>jar</packaging>
   <name>Ambari Server</name>
   <name>Ambari Server</name>
-  <version>1.0.3-SNAPSHOT</version>
+  <version>1.0.3</version>
   <description>Ambari Server</description>
   <description>Ambari Server</description>
   <build>
   <build>
     <plugins>
     <plugins>
@@ -75,6 +75,7 @@
           </workarea>
           </workarea>
           -->
           -->
           <copyright>2012, Apache Software Foundation</copyright>
           <copyright>2012, Apache Software Foundation</copyright>
+          <version>${project.version}</version>
           <group>Development</group>
           <group>Development</group>
           <description>Maven Recipe: RPM Package.</description>
           <description>Maven Recipe: RPM Package.</description>
           <mappings>
           <mappings>
@@ -155,9 +156,25 @@
                 <source>
                 <source>
                   <location>src/main/resources/Ambari-DDL.sql</location>
                   <location>src/main/resources/Ambari-DDL.sql</location>
                 </source>
                 </source>
+                <source>
+                  <location>src/main/resources/stacks</location>
+                </source>
+              </sources>
+            </mapping>
+
+            <mapping>
+              <directory>/usr/lib/python2.6/site-packages/ambari_server</directory>
+              <sources>
+                <source>
+                  <location>src/main/python/bootstrap.py</location>
+                </source>
+                <source>
+                  <location>src/main/python/setupAgent.py</location>
+                </source>
               </sources>
               </sources>
             </mapping>
             </mapping>
 
 
+
             <mapping>
             <mapping>
               <directory>/var/run/ambari-server</directory>
               <directory>/var/run/ambari-server</directory>
             </mapping>
             </mapping>

+ 6 - 7
ambari-server/src/main/java/org/apache/ambari/server/api/handlers/BaseManagementHandler.java

@@ -23,10 +23,13 @@ import org.apache.ambari.server.api.services.Request;
 import org.apache.ambari.server.api.services.Result;
 import org.apache.ambari.server.api.services.Result;
 import org.apache.ambari.server.api.services.ResultImpl;
 import org.apache.ambari.server.api.services.ResultImpl;
 import org.apache.ambari.server.api.util.TreeNode;
 import org.apache.ambari.server.api.util.TreeNode;
+import org.apache.ambari.server.controller.spi.PropertyId;
 import org.apache.ambari.server.controller.spi.RequestStatus;
 import org.apache.ambari.server.controller.spi.RequestStatus;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 
 
+import java.util.Collections;
+import java.util.Map;
 import java.util.Set;
 import java.util.Set;
 
 
 /**
 /**
@@ -36,10 +39,8 @@ public class BaseManagementHandler implements RequestHandler {
   @Override
   @Override
   public Result handleRequest(Request request) {
   public Result handleRequest(Request request) {
     ResourceDefinition resource = request.getResourceDefinition();
     ResourceDefinition resource = request.getResourceDefinition();
-    resource.setProperties(request.getHttpBodyProperties());
-    RequestStatus status = request.getPersistenceManager().persist(resource);
-
-    return createResult(request, status);
+    return createResult(request, request.getPersistenceManager().persist(
+        resource, request.getHttpBodyProperties()));
   }
   }
 
 
   private Result createResult(Request request, RequestStatus requestStatus) {
   private Result createResult(Request request, RequestStatus requestStatus) {
@@ -62,14 +63,12 @@ public class BaseManagementHandler implements RequestHandler {
     if (! isSynchronous) {
     if (! isSynchronous) {
       Resource requestResource = requestStatus.getRequestResource();
       Resource requestResource = requestStatus.getRequestResource();
       TreeNode<Resource> r = tree.addChild(requestResource, "request");
       TreeNode<Resource> r = tree.addChild(requestResource, "request");
-      String requestHref = buildRequestHref(request, requestStatus);
-      r.setProperty("href", requestHref);
+      r.setProperty("href", buildRequestHref(request, requestStatus));
     }
     }
 
 
     return result;
     return result;
   }
   }
 
 
-  //todo: this needs to be rewritten and needs to support operating on clusters collection
   private String buildRequestHref(Request request, RequestStatus requestStatus) {
   private String buildRequestHref(Request request, RequestStatus requestStatus) {
     StringBuilder sb = new StringBuilder();
     StringBuilder sb = new StringBuilder();
     String origHref = request.getURI();
     String origHref = request.getURI();

+ 25 - 17
ambari-server/src/main/java/org/apache/ambari/server/api/query/QueryImpl.java

@@ -21,7 +21,6 @@ package org.apache.ambari.server.api.query;
 import org.apache.ambari.server.api.resources.ResourceDefinition;
 import org.apache.ambari.server.api.resources.ResourceDefinition;
 import org.apache.ambari.server.api.services.ResultImpl;
 import org.apache.ambari.server.api.services.ResultImpl;
 import org.apache.ambari.server.api.util.TreeNodeImpl;
 import org.apache.ambari.server.api.util.TreeNodeImpl;
-import org.apache.ambari.server.controller.internal.ClusterControllerImpl;
 import org.apache.ambari.server.controller.internal.PropertyIdImpl;
 import org.apache.ambari.server.controller.internal.PropertyIdImpl;
 import org.apache.ambari.server.controller.utilities.ClusterControllerHelper;
 import org.apache.ambari.server.controller.utilities.ClusterControllerHelper;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
@@ -32,7 +31,6 @@ import org.apache.ambari.server.controller.predicate.EqualsPredicate;
 import org.apache.ambari.server.api.services.Result;
 import org.apache.ambari.server.api.services.Result;
 import org.apache.ambari.server.controller.spi.*;
 import org.apache.ambari.server.controller.spi.*;
 import org.apache.ambari.server.api.util.TreeNode;
 import org.apache.ambari.server.api.util.TreeNode;
-import org.mortbay.log.Log;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 
 
@@ -155,21 +153,12 @@ public class QueryImpl implements Query {
       // add a child node for the resource and provide a unique name.  The name is never used.
       // add a child node for the resource and provide a unique name.  The name is never used.
       //todo: provide a more meaningful node name
       //todo: provide a more meaningful node name
       TreeNode<Resource> node = tree.addChild(resource, resource.getType() + ":" + count++);
       TreeNode<Resource> node = tree.addChild(resource, resource.getType() + ":" + count++);
-      LOG.info("Resource object resource " + resource);
        for (Map.Entry<String, ResourceDefinition> entry : m_mapSubResources.entrySet()) {
        for (Map.Entry<String, ResourceDefinition> entry : m_mapSubResources.entrySet()) {
         String subResCategory = entry.getKey();
         String subResCategory = entry.getKey();
         ResourceDefinition r = entry.getValue();
         ResourceDefinition r = entry.getValue();
-        
-        r.setParentId(m_resourceDefinition.getType(), (String) (resource.getPropertyValue(
-            getClusterController().getSchema(m_resourceDefinition.getType()).
-                getKeyPropertyId(m_resourceDefinition.getType())).toString()));
-        
-        LOG.info("Setting various values for resource " + r.getId());
-        if (r.getResourceIds() != null) {
-          for (Map.Entry<Resource.Type, String> tentry: r.getResourceIds().entrySet()) {
-            LOG.info("Resource Id's " + tentry.getKey() + " value " + tentry.getValue());
-          }
-        }
+
+        setParentIdsOnSubResource(resource, r);
+
         TreeNode<Resource> childResult = r.getQuery().execute().getResultTree();
         TreeNode<Resource> childResult = r.getQuery().execute().getResultTree();
         childResult.setName(subResCategory);
         childResult.setName(subResCategory);
         childResult.setProperty("isCollection", "false");
         childResult.setProperty("isCollection", "false");
@@ -178,7 +167,6 @@ public class QueryImpl implements Query {
     }
     }
     return result;
     return result;
   }
   }
-
   @Override
   @Override
   public Predicate getInternalPredicate() {
   public Predicate getInternalPredicate() {
     return createInternalPredicate(m_resourceDefinition);
     return createInternalPredicate(m_resourceDefinition);
@@ -272,7 +260,6 @@ public class QueryImpl implements Query {
   }
   }
 
 
   private BasePredicate createInternalPredicate(ResourceDefinition resourceDefinition) {
   private BasePredicate createInternalPredicate(ResourceDefinition resourceDefinition) {
-    //todo: account for user predicates
     Resource.Type resourceType = resourceDefinition.getType();
     Resource.Type resourceType = resourceDefinition.getType();
     Map<Resource.Type, String> mapResourceIds = resourceDefinition.getResourceIds();
     Map<Resource.Type, String> mapResourceIds = resourceDefinition.getResourceIds();
     Schema schema = getClusterController().getSchema(resourceType);
     Schema schema = getClusterController().getSchema(resourceType);
@@ -283,7 +270,10 @@ public class QueryImpl implements Query {
       //todo: host_component queries and host is not available for component queries.
       //todo: host_component queries and host is not available for component queries.
       //todo: this should be rectified when the data model is changed for host_component
       //todo: this should be rectified when the data model is changed for host_component
       if (entry.getValue() != null) {
       if (entry.getValue() != null) {
-        setPredicates.add(new EqualsPredicate(schema.getKeyPropertyId(entry.getKey()), entry.getValue()));
+        PropertyId keyPropertyId = schema.getKeyPropertyId(entry.getKey());
+        if (keyPropertyId != null) {
+          setPredicates.add(new EqualsPredicate(keyPropertyId, entry.getValue()));
+        }
       }
       }
     }
     }
 
 
@@ -358,6 +348,24 @@ public class QueryImpl implements Query {
     return r;
     return r;
   }
   }
 
 
+  private void setParentIdsOnSubResource(Resource resource, ResourceDefinition r) {
+    Map<Resource.Type, String> mapParentIds = m_resourceDefinition.getResourceIds();
+    Map<Resource.Type, String> mapResourceIds = new HashMap<Resource.Type, String>(mapParentIds.size());
+    for (Map.Entry<Resource.Type, String> resourceIdEntry : mapParentIds.entrySet()) {
+      Resource.Type type = resourceIdEntry.getKey();
+      String value = resourceIdEntry.getValue();
+
+      if (value == null) {
+        Object o = resource.getPropertyValue(getClusterController().getSchema(type).getKeyPropertyId(type));
+        value = o == null ? null : o.toString();
+      }
+      if (value != null) {
+        mapResourceIds.put(type, value);
+      }
+    }
+    r.setParentIds(mapResourceIds);
+  }
+
   Result createResult() {
   Result createResult() {
     return new ResultImpl(true);
     return new ResultImpl(true);
   }
   }

+ 4 - 22
ambari-server/src/main/java/org/apache/ambari/server/api/resources/BaseResourceDefinition.java

@@ -56,10 +56,6 @@ public abstract class BaseResourceDefinition implements ResourceDefinition {
    */
    */
   private Map<Resource.Type, String> m_mapResourceIds = new HashMap<Resource.Type, String>();
   private Map<Resource.Type, String> m_mapResourceIds = new HashMap<Resource.Type, String>();
 
 
-  //TODO: Refactor out of this class when setProperties is moved.
-  private Map<PropertyId, Object> m_properties = new HashMap<PropertyId, Object>();
-
-
   /**
   /**
    * Constructor.
    * Constructor.
    *
    *
@@ -73,8 +69,10 @@ public abstract class BaseResourceDefinition implements ResourceDefinition {
   }
   }
 
 
   @Override
   @Override
-  public void setParentId(Resource.Type type, String value) {
-    setResourceId(type, value);
+  public void setParentIds(Map<Resource.Type, String> mapIds) {
+    for (Map.Entry<Resource.Type, String> entry : mapIds.entrySet() ) {
+      setResourceId(entry.getKey(), entry.getValue());
+    }
   }
   }
 
 
   @Override
   @Override
@@ -119,22 +117,6 @@ public abstract class BaseResourceDefinition implements ResourceDefinition {
     return listProcessors;
     return listProcessors;
   }
   }
 
 
-  //todo: refactor set/get property methods out of this class
-  @Override
-  public void setProperty(PropertyId property, String value) {
-    m_properties.put(property, value);
-  }
-
-  @Override
-  public void setProperties(Map<PropertyId, String> mapProperties) {
-    m_properties.putAll(mapProperties);
-  }
-
-  @Override
-  public Map<PropertyId, Object> getProperties() {
-    return m_properties;
-  }
-
   @Override
   @Override
   public boolean equals(Object o) {
   public boolean equals(Object o) {
     if (this == o) return true;
     if (this == o) return true;

+ 1 - 1
ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java

@@ -61,7 +61,7 @@ public class ClusterResourceDefinition extends BaseResourceDefinition {
         Resource.Type.Service).getKeyPropertyId(Resource.Type.Service));
         Resource.Type.Service).getKeyPropertyId(Resource.Type.Service));
     mapChildren.put(serviceResource.getPluralName(), serviceResource);
     mapChildren.put(serviceResource.getPluralName(), serviceResource);
 
 
-    HostResourceDefinition hostResource = new HostResourceDefinition(null, getId());
+    HostResourceDefinition hostResource = new HostResourceDefinition(null, getId(), true);
     hostResource.getQuery().addProperty(getClusterController().getSchema(
     hostResource.getQuery().addProperty(getClusterController().getSchema(
         Resource.Type.Host).getKeyPropertyId(Resource.Type.Host));
         Resource.Type.Host).getKeyPropertyId(Resource.Type.Host));
     mapChildren.put(hostResource.getPluralName(), hostResource);
     mapChildren.put(hostResource.getPluralName(), hostResource);

+ 10 - 17
ambari-server/src/main/java/org/apache/ambari/server/api/resources/ComponentResourceDefinition.java

@@ -38,12 +38,6 @@ public class ComponentResourceDefinition extends BaseResourceDefinition {
    */
    */
   private String m_clusterId;
   private String m_clusterId;
 
 
-  /**
-   * value of serviceId foreign key
-   */
-  private String m_serviceId;
-
-
   /**
   /**
    * Constructor.
    * Constructor.
    *
    *
@@ -54,9 +48,9 @@ public class ComponentResourceDefinition extends BaseResourceDefinition {
   public ComponentResourceDefinition(String id, String clusterId, String serviceId) {
   public ComponentResourceDefinition(String id, String clusterId, String serviceId) {
     super(Resource.Type.Component, id);
     super(Resource.Type.Component, id);
     m_clusterId = clusterId;
     m_clusterId = clusterId;
-    m_serviceId = serviceId;
+
     setResourceId(Resource.Type.Cluster, m_clusterId);
     setResourceId(Resource.Type.Cluster, m_clusterId);
-    setResourceId(Resource.Type.Service, m_serviceId);
+    setResourceId(Resource.Type.Service, serviceId);
   }
   }
 
 
   @Override
   @Override
@@ -94,17 +88,17 @@ public class ComponentResourceDefinition extends BaseResourceDefinition {
   }
   }
 
 
   @Override
   @Override
-  public void setParentId(Resource.Type type, String value) {
-    if (type == Resource.Type.HostComponent) {
-      setId(value);
-    } else {
-      super.setParentId(type, value);
+  public void setParentIds(Map<Resource.Type, String> mapIds) {
+    String id = mapIds.remove(Resource.Type.HostComponent);
+    if (id != null) {
+      setId(id);
     }
     }
+    super.setParentIds(mapIds);
   }
   }
 
 
   /**
   /**
-   * Base resource processor which generates href's.  This is called by the {@link org.apache.ambari.server.api.services.ResultPostProcessor} during post
-   * processing of a result.
+   * Base resource processor which generates href's.  This is called by the
+   * {@link org.apache.ambari.server.api.services.ResultPostProcessor} during post processing of a result.
    */
    */
   private class ComponentHrefProcessor extends BaseHrefPostProcessor {
   private class ComponentHrefProcessor extends BaseHrefPostProcessor {
     @Override
     @Override
@@ -113,12 +107,11 @@ public class ComponentResourceDefinition extends BaseResourceDefinition {
 
 
       if (parent.getParent() != null && parent.getParent().getObject().getType() == Resource.Type.HostComponent) {
       if (parent.getParent() != null && parent.getParent().getObject().getType() == Resource.Type.HostComponent) {
         Resource r = resultNode.getObject();
         Resource r = resultNode.getObject();
-        String clusterId = getResourceIds().get(Resource.Type.Cluster);
         Schema schema = ClusterControllerHelper.getClusterController().getSchema(r.getType());
         Schema schema = ClusterControllerHelper.getClusterController().getSchema(r.getType());
         Object serviceId = r.getPropertyValue(schema.getKeyPropertyId(Resource.Type.Service));
         Object serviceId = r.getPropertyValue(schema.getKeyPropertyId(Resource.Type.Service));
         Object componentId = r.getPropertyValue(schema.getKeyPropertyId(r.getType()));
         Object componentId = r.getPropertyValue(schema.getKeyPropertyId(r.getType()));
 
 
-        href = href.substring(0, href.indexOf(clusterId) + clusterId.length() + 1) +
+        href = href.substring(0, href.indexOf("/hosts/") + 1) +
             "services/" + serviceId + "/components/" + componentId;
             "services/" + serviceId + "/components/" + componentId;
 
 
         resultNode.setProperty("href", href);
         resultNode.setProperty("href", href);

+ 8 - 9
ambari-server/src/main/java/org/apache/ambari/server/api/resources/ConfigurationResourceDefinition.java

@@ -49,9 +49,6 @@ public class ConfigurationResourceDefinition extends BaseResourceDefinition {
     super(Resource.Type.Configuration, configType);
     super(Resource.Type.Configuration, configType);
     m_clusterId = clusterId;
     m_clusterId = clusterId;
     setResourceId(Resource.Type.Cluster, m_clusterId);
     setResourceId(Resource.Type.Cluster, m_clusterId);
-
-    if (null != configTag)
-      setProperty(PropertyHelper.getPropertyId("tag", "Config"), configTag);
   }
   }
 
 
   @Override
   @Override
@@ -83,15 +80,17 @@ public class ConfigurationResourceDefinition extends BaseResourceDefinition {
     public void process(Request request, TreeNode<Resource> resultNode, String href) {
     public void process(Request request, TreeNode<Resource> resultNode, String href) {
       if (resultNode.getObject().getType() == Resource.Type.Configuration) {
       if (resultNode.getObject().getType() == Resource.Type.Configuration) {
 
 
-        String clusterId = getResourceIds().get(Resource.Type.Cluster);
-        String type = (String) resultNode.getObject().getPropertyValue(PropertyHelper.getPropertyId("type"));
-        String tag = (String) resultNode.getObject().getPropertyValue(PropertyHelper.getPropertyId("tag"));
-
         if (! href.endsWith("/")) {
         if (! href.endsWith("/")) {
           href += '/';
           href += '/';
         }
         }
-        href = href.substring(0, href.indexOf(clusterId) + clusterId.length() + 1) +
-            "configurations?type=" + type + "&tag=" + tag;
+
+        String clustersToken = "/clusters";
+        int idx = href.indexOf(clustersToken) + clustersToken.length() + 1;
+        idx = href.indexOf("/", idx) + 1;
+
+        String type = (String) resultNode.getObject().getPropertyValue(PropertyHelper.getPropertyId("type"));
+        String tag = (String) resultNode.getObject().getPropertyValue(PropertyHelper.getPropertyId("tag"));
+        href = href.substring(0, idx) + "configurations?type=" + type + "&tag=" + tag;
 
 
         resultNode.setProperty("href", href);
         resultNode.setProperty("href", href);
       } else {
       } else {

+ 7 - 14
ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostComponentResourceDefinition.java

@@ -38,11 +38,6 @@ public class HostComponentResourceDefinition extends BaseResourceDefinition {
    */
    */
   private String m_clusterId;
   private String m_clusterId;
 
 
-  /**
-   * value of host id foreign key
-   */
-  private String m_hostId;
-
 
 
   /**
   /**
    * Constructor.
    * Constructor.
@@ -54,9 +49,8 @@ public class HostComponentResourceDefinition extends BaseResourceDefinition {
   public HostComponentResourceDefinition(String id, String clusterId, String hostId) {
   public HostComponentResourceDefinition(String id, String clusterId, String hostId) {
     super(Resource.Type.HostComponent, id);
     super(Resource.Type.HostComponent, id);
     m_clusterId = clusterId;
     m_clusterId = clusterId;
-    m_hostId = hostId;
     setResourceId(Resource.Type.Cluster, m_clusterId);
     setResourceId(Resource.Type.Cluster, m_clusterId);
-    setResourceId(Resource.Type.Host, m_hostId);
+    setResourceId(Resource.Type.Host, hostId);
   }
   }
 
 
   @Override
   @Override
@@ -94,12 +88,12 @@ public class HostComponentResourceDefinition extends BaseResourceDefinition {
   }
   }
 
 
   @Override
   @Override
-  public void setParentId(Resource.Type type, String value) {
-    if (type == Resource.Type.Component) {
-      setId(value);
-    } else {
-      super.setParentId(type, value);
+  public void setParentIds(Map<Resource.Type, String> mapIds) {
+    String id = mapIds.remove(Resource.Type.Component);
+    if (id != null) {
+      setId(id);
     }
     }
+    super.setParentIds(mapIds);
   }
   }
 
 
 
 
@@ -114,12 +108,11 @@ public class HostComponentResourceDefinition extends BaseResourceDefinition {
 
 
       if (parent.getParent() != null && parent.getParent().getObject().getType() == Resource.Type.Component) {
       if (parent.getParent() != null && parent.getParent().getObject().getType() == Resource.Type.Component) {
         Resource r = resultNode.getObject();
         Resource r = resultNode.getObject();
-        String clusterId = getResourceIds().get(Resource.Type.Cluster);
         Schema schema = ClusterControllerHelper.getClusterController().getSchema(r.getType());
         Schema schema = ClusterControllerHelper.getClusterController().getSchema(r.getType());
         Object host = r.getPropertyValue(schema.getKeyPropertyId(Resource.Type.Host));
         Object host = r.getPropertyValue(schema.getKeyPropertyId(Resource.Type.Host));
         Object hostComponent = r.getPropertyValue(schema.getKeyPropertyId(r.getType()));
         Object hostComponent = r.getPropertyValue(schema.getKeyPropertyId(r.getType()));
 
 
-        href = href.substring(0, href.indexOf(clusterId) + clusterId.length() + 1) +
+        href = href.substring(0, href.indexOf("/services/") + 1) +
             "hosts/" + host + "/host_components/" + hostComponent;
             "hosts/" + host + "/host_components/" + hostComponent;
 
 
         resultNode.setProperty("href", href);
         resultNode.setProperty("href", href);

+ 9 - 3
ambari-server/src/main/java/org/apache/ambari/server/api/resources/HostResourceDefinition.java

@@ -36,14 +36,21 @@ public class HostResourceDefinition extends BaseResourceDefinition {
    */
    */
   private String m_clusterId;
   private String m_clusterId;
 
 
+  /**
+   * Whether the host resource is associated with a cluster.
+   */
+  private boolean m_attached;
+
   /**
   /**
    * Constructor.
    * Constructor.
    *
    *
    * @param id        host id value
    * @param id        host id value
    * @param clusterId cluster id value
    * @param clusterId cluster id value
+   * @param attached
    */
    */
-  public HostResourceDefinition(String id, String clusterId) {
+  public HostResourceDefinition(String id, String clusterId, boolean attached) {
     super(Resource.Type.Host, id);
     super(Resource.Type.Host, id);
+    m_attached = attached;
     m_clusterId = clusterId;
     m_clusterId = clusterId;
     setResourceId(Resource.Type.Cluster, m_clusterId);
     setResourceId(Resource.Type.Cluster, m_clusterId);
     
     
@@ -73,8 +80,7 @@ public class HostResourceDefinition extends BaseResourceDefinition {
   public Map<String, ResourceDefinition> getSubResources() {
   public Map<String, ResourceDefinition> getSubResources() {
     Map<String, ResourceDefinition> mapChildren = new HashMap<String, ResourceDefinition>();
     Map<String, ResourceDefinition> mapChildren = new HashMap<String, ResourceDefinition>();
 
 
-    // !!! is this a host for a cluster
-    if (null != m_clusterId) {
+    if (m_attached) {
       HostComponentResourceDefinition hostComponentResource =
       HostComponentResourceDefinition hostComponentResource =
           new HostComponentResourceDefinition(null, m_clusterId, getId());
           new HostComponentResourceDefinition(null, m_clusterId, getId());
       hostComponentResource.getQuery().addProperty(getClusterController().getSchema(
       hostComponentResource.getQuery().addProperty(getClusterController().getSchema(

+ 3 - 27
ambari-server/src/main/java/org/apache/ambari/server/api/resources/ResourceDefinition.java

@@ -63,12 +63,11 @@ public interface ResourceDefinition {
   public Resource.Type getType();
   public Resource.Type getType();
 
 
   /**
   /**
-   * Set the value of the parent foreign key.
+   * Set the values of the parent foreign keys.
    *
    *
-   * @param type  resource type of the parent
-   * @param value vale of the parent id
+   * @param mapIds  map of all parent foreign keys. Map from resource type to id value.
    */
    */
-  public void setParentId(Resource.Type type, String value);
+  public void setParentIds(Map<Resource.Type, String> mapIds);
 
 
   /**
   /**
    * Obtain the primary and foreign key properties for the resource.
    * Obtain the primary and foreign key properties for the resource.
@@ -101,29 +100,6 @@ public interface ResourceDefinition {
    */
    */
   public List<PostProcessor> getPostProcessors();
   public List<PostProcessor> getPostProcessors();
 
 
-  //TODO: refactor set/get Property methods out of this class
-  /**
-   * Set a property on this resource.
-   *
-   * @param property the property
-   * @param value    the value
-   */
-  public void setProperty(PropertyId property, String value);
-
-  /**
-   * Set a map of properties on the resource.
-   *
-   * @param mapProperties a map of properties
-   */
-  public void setProperties(Map<PropertyId, String> mapProperties);
-
-  /**
-   * Get the properties which have been set on this resource.
-   *
-   * @return the properties which have been set on this resource
-   */
-  public Map<PropertyId, Object> getProperties();
-
   /**
   /**
    * Resource specific result processor.
    * Resource specific result processor.
    * Used to provide resource specific processing of a result.
    * Used to provide resource specific processing of a result.

+ 3 - 2
ambari-server/src/main/java/org/apache/ambari/server/api/services/BasePersistenceManager.java

@@ -26,6 +26,7 @@ import org.apache.ambari.server.controller.spi.Request;
 
 
 import java.util.Collections;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Map;
+import java.util.Set;
 
 
 /**
 /**
  * Base PersistenceManager functionality.
  * Base PersistenceManager functionality.
@@ -36,7 +37,7 @@ public abstract class BasePersistenceManager implements PersistenceManager {
     return ClusterControllerHelper.getClusterController();
     return ClusterControllerHelper.getClusterController();
   }
   }
 
 
-  protected Request createControllerRequest(Map<PropertyId, Object> properties) {
-    return PropertyHelper.getCreateRequest(Collections.singleton(properties));
+  protected Request createControllerRequest(Set<Map<PropertyId, Object>> setProperties) {
+    return PropertyHelper.getCreateRequest(setProperties);
   }
   }
 }
 }

+ 19 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/ComponentService.java

@@ -83,6 +83,25 @@ public class ComponentService extends BaseService {
         createResourceDefinition(null, m_clusterName, m_serviceName));
         createResourceDefinition(null, m_clusterName, m_serviceName));
   }
   }
 
 
+  /**
+   * Handles: POST /clusters/{clusterID}/services/{serviceID}/components
+   * Create components by specifying an array of components in the http body.
+   * This is used to create multiple components in a single request.
+   *
+   * @param body          http body
+   * @param headers       http headers
+   * @param ui            uri info
+   *
+   * @return status code only, 201 if successful
+   */
+  @POST
+  @Produces("text/plain")
+  public Response createComponents(String body, @Context HttpHeaders headers, @Context UriInfo ui) {
+
+    return handleRequest(headers, body, ui, Request.Type.POST,
+        createResourceDefinition(null, m_clusterName, m_serviceName));
+  }
+
   /**
   /**
    * Handles: POST /clusters/{clusterID}/services/{serviceID}/components/{componentID}
    * Handles: POST /clusters/{clusterID}/services/{serviceID}/components/{componentID}
    * Create a specific component.
    * Create a specific component.

+ 9 - 8
ambari-server/src/main/java/org/apache/ambari/server/api/services/ConfigurationService.java

@@ -59,11 +59,12 @@ public class ConfigurationService extends BaseService {
   @Produces("text/plain")
   @Produces("text/plain")
   public Response getConfigurations(@Context HttpHeaders headers, @Context UriInfo ui) {
   public Response getConfigurations(@Context HttpHeaders headers, @Context UriInfo ui) {
     return handleRequest(headers, null, ui, Request.Type.GET,
     return handleRequest(headers, null, ui, Request.Type.GET,
-        createResourceDefinition(null, null, m_clusterName));
+        createResourceDefinition(null, m_clusterName));
   }
   }
 
 
   /**
   /**
-   * Handles URL: /clusters/{clusterId}/configurations.  The body should contain:
+   * Handles URL: /clusters/{clusterId}/configurations
+   * The body should contain:
    * <pre>
    * <pre>
    * {
    * {
    *     "type":"type_string",
    *     "type":"type_string",
@@ -76,30 +77,30 @@ public class ConfigurationService extends BaseService {
    *     }
    *     }
    * }
    * }
    * </pre>
    * </pre>
-   * Get all services for a cluster.
+   *
+   * To create multiple configurations is a request, provide an array of configuration properties.
    *
    *
    * @param headers http headers
    * @param headers http headers
    * @param ui      uri info
    * @param ui      uri info
-   * @return service collection resource representation
+   * @return status code only, 201 if successful
    */
    */
   @POST
   @POST
   @Produces("text/plain")
   @Produces("text/plain")
   public Response createConfigurations(String body,@Context HttpHeaders headers, @Context UriInfo ui) {
   public Response createConfigurations(String body,@Context HttpHeaders headers, @Context UriInfo ui) {
 
 
     return handleRequest(headers, body, ui, Request.Type.POST,
     return handleRequest(headers, body, ui, Request.Type.POST,
-        createResourceDefinition(null, null, m_clusterName));
+        createResourceDefinition(null, m_clusterName));
   }
   }
 
 
   /**
   /**
    * Create a service resource definition.
    * Create a service resource definition.
    *
    *
    * @param configType  configuration type
    * @param configType  configuration type
-   * @param configTag   tag applied to the configuration
    * @param clusterName cluster name
    * @param clusterName cluster name
    *
    *
    * @return a service resource definition
    * @return a service resource definition
    */
    */
-  ResourceDefinition createResourceDefinition(String configType, String configTag, String clusterName) {
-    return new ConfigurationResourceDefinition(configType, configTag, clusterName);
+  ResourceDefinition createResourceDefinition(String configType, String clusterName) {
+    return new ConfigurationResourceDefinition(configType, null, clusterName);
   }
   }
 }
 }

+ 17 - 8
ambari-server/src/main/java/org/apache/ambari/server/api/services/CreatePersistenceManager.java

@@ -20,29 +20,38 @@ package org.apache.ambari.server.api.services;
 
 
 import org.apache.ambari.server.api.resources.ResourceDefinition;
 import org.apache.ambari.server.api.resources.ResourceDefinition;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.AmbariException;
-import org.apache.ambari.server.controller.spi.ClusterController;
-import org.apache.ambari.server.controller.spi.RequestStatus;
-import org.apache.ambari.server.controller.spi.Resource;
-import org.apache.ambari.server.controller.spi.Schema;
+import org.apache.ambari.server.controller.spi.*;
 
 
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Map;
+import java.util.Set;
 
 
 /**
 /**
  * Responsible for persisting the creation of a resource in the back end.
  * Responsible for persisting the creation of a resource in the back end.
  */
  */
 public class CreatePersistenceManager extends BasePersistenceManager {
 public class CreatePersistenceManager extends BasePersistenceManager {
   @Override
   @Override
-  public RequestStatus persist(ResourceDefinition resource) {
+  public RequestStatus persist(ResourceDefinition resource, Set<Map<PropertyId, Object>> setProperties) {
     ClusterController controller = getClusterController();
     ClusterController controller = getClusterController();
     Map<Resource.Type, String> mapResourceIds = resource.getResourceIds();
     Map<Resource.Type, String> mapResourceIds = resource.getResourceIds();
     Resource.Type type = resource.getType();
     Resource.Type type = resource.getType();
     Schema schema = controller.getSchema(type);
     Schema schema = controller.getSchema(type);
 
 
-    for (Map.Entry<Resource.Type, String> entry : mapResourceIds.entrySet()) {
-      resource.setProperty(schema.getKeyPropertyId(entry.getKey()), entry.getValue());
+    if (setProperties.size() == 0) {
+      setProperties.add(new HashMap<PropertyId, Object>());
     }
     }
+
+    for (Map<PropertyId, Object> mapProperties : setProperties) {
+      for (Map.Entry<Resource.Type, String> entry : mapResourceIds.entrySet()) {
+        PropertyId property = schema.getKeyPropertyId(entry.getKey());
+        if (! mapProperties.containsKey(property)) {
+          mapProperties.put(property, entry.getValue());
+        }
+      }
+    }
+
     try {
     try {
-      return controller.createResources(type, createControllerRequest(resource.getProperties()));
+      return controller.createResources(type, createControllerRequest(setProperties));
     } catch (AmbariException e) {
     } catch (AmbariException e) {
       //todo: handle exception
       //todo: handle exception
       throw new RuntimeException("Create of resource failed: " + e, e);
       throw new RuntimeException("Create of resource failed: " + e, e);

+ 5 - 1
ambari-server/src/main/java/org/apache/ambari/server/api/services/DeletePersistenceManager.java

@@ -20,15 +20,19 @@ package org.apache.ambari.server.api.services;
 
 
 import org.apache.ambari.server.api.resources.ResourceDefinition;
 import org.apache.ambari.server.api.resources.ResourceDefinition;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.spi.PropertyId;
 import org.apache.ambari.server.controller.spi.RequestStatus;
 import org.apache.ambari.server.controller.spi.RequestStatus;
 
 
+import java.util.Map;
+import java.util.Set;
+
 
 
 /**
 /**
  * Responsible for persisting the deletion of a resource in the back end.
  * Responsible for persisting the deletion of a resource in the back end.
  */
  */
 public class DeletePersistenceManager extends BasePersistenceManager {
 public class DeletePersistenceManager extends BasePersistenceManager {
   @Override
   @Override
-  public RequestStatus persist(ResourceDefinition resource) {
+  public RequestStatus persist(ResourceDefinition resource, Set<Map<PropertyId, Object>> setProperties) {
     try {
     try {
       //todo: need to account for multiple resources and user predicate
       //todo: need to account for multiple resources and user predicate
       return getClusterController().deleteResources(resource.getType(),
       return getClusterController().deleteResources(resource.getType(),

+ 19 - 0
ambari-server/src/main/java/org/apache/ambari/server/api/services/HostComponentService.java

@@ -83,6 +83,25 @@ public class HostComponentService extends BaseService {
         createResourceDefinition(null, m_clusterName, m_hostName));
         createResourceDefinition(null, m_clusterName, m_hostName));
   }
   }
 
 
+  /**
+   * Handles POST /clusters/{clusterID}/hosts/{hostID}/host_components
+   * Create host components by specifying an array of host components in the http body.
+   * This is used to create multiple host components in a single request.
+   *
+   * @param body              http body
+   * @param headers           http headers
+   * @param ui                uri info
+   *
+   * @return status code only, 201 if successful
+   */
+  @POST
+  @Produces("text/plain")
+  public Response createHostComponents(String body, @Context HttpHeaders headers, @Context UriInfo ui) {
+
+    return handleRequest(headers, body, ui, Request.Type.POST,
+        createResourceDefinition(null, m_clusterName, m_hostName));
+  }
+
   /**
   /**
    * Handles POST /clusters/{clusterID}/hosts/{hostID}/host_components/{hostComponentID}
    * Handles POST /clusters/{clusterID}/hosts/{hostID}/host_components/{hostComponentID}
    * Create a specific host_component.
    * Create a specific host_component.

+ 30 - 10
ambari-server/src/main/java/org/apache/ambari/server/api/services/HostService.java

@@ -76,7 +76,7 @@ public class HostService extends BaseService {
                           @PathParam("hostName") String hostName) {
                           @PathParam("hostName") String hostName) {
 
 
     return handleRequest(headers, null, ui, Request.Type.GET,
     return handleRequest(headers, null, ui, Request.Type.GET,
-        createResourceDefinition(hostName, m_clusterName));
+        createResourceDefinition(hostName, m_clusterName, ui));
   }
   }
 
 
   /**
   /**
@@ -90,7 +90,26 @@ public class HostService extends BaseService {
   @GET
   @GET
   @Produces("text/plain")
   @Produces("text/plain")
   public Response getHosts(@Context HttpHeaders headers, @Context UriInfo ui) {
   public Response getHosts(@Context HttpHeaders headers, @Context UriInfo ui) {
-    return handleRequest(headers, null, ui, Request.Type.GET, createResourceDefinition(null, m_clusterName));
+    return handleRequest(headers, null, ui, Request.Type.GET, createResourceDefinition(null, m_clusterName, ui));
+  }
+
+  /**
+   * Handles POST /clusters/{clusterID}/hosts
+   * Create hosts by specifying an array of hosts in the http body.
+   * This is used to create multiple hosts in a single request.
+   *
+   * @param body     http body
+   * @param headers  http headers
+   * @param ui       uri info
+   *
+   * @return status code only, 201 if successful
+   */
+  @POST
+  @Produces("text/plain")
+  public Response createHosts(String body, @Context HttpHeaders headers, @Context UriInfo ui) {
+
+    return handleRequest(headers, body, ui, Request.Type.POST,
+        createResourceDefinition(null, m_clusterName, ui));
   }
   }
 
 
   /**
   /**
@@ -111,7 +130,7 @@ public class HostService extends BaseService {
                           @PathParam("hostName") String hostName) {
                           @PathParam("hostName") String hostName) {
 
 
     return handleRequest(headers, body, ui, Request.Type.POST,
     return handleRequest(headers, body, ui, Request.Type.POST,
-        createResourceDefinition(hostName, m_clusterName));
+        createResourceDefinition(hostName, m_clusterName, ui));
   }
   }
 
 
   /**
   /**
@@ -132,7 +151,7 @@ public class HostService extends BaseService {
                           @PathParam("hostName") String hostName) {
                           @PathParam("hostName") String hostName) {
 
 
     return handleRequest(headers, body, ui, Request.Type.PUT,
     return handleRequest(headers, body, ui, Request.Type.PUT,
-        createResourceDefinition(hostName, m_clusterName));
+        createResourceDefinition(hostName, m_clusterName, ui));
   }
   }
 
 
   /**
   /**
@@ -150,7 +169,7 @@ public class HostService extends BaseService {
   public Response updateHosts(String body, @Context HttpHeaders headers, @Context UriInfo ui) {
   public Response updateHosts(String body, @Context HttpHeaders headers, @Context UriInfo ui) {
 
 
     return handleRequest(headers, body, ui, Request.Type.PUT,
     return handleRequest(headers, body, ui, Request.Type.PUT,
-        createResourceDefinition(null, m_clusterName));
+        createResourceDefinition(null, m_clusterName, ui));
   }
   }
 
 
   /**
   /**
@@ -170,7 +189,7 @@ public class HostService extends BaseService {
                              @PathParam("hostName") String hostName) {
                              @PathParam("hostName") String hostName) {
 
 
     return handleRequest(headers, null, ui, Request.Type.DELETE,
     return handleRequest(headers, null, ui, Request.Type.DELETE,
-        createResourceDefinition(hostName, m_clusterName));
+        createResourceDefinition(hostName, m_clusterName, ui));
   }
   }
 
 
   /**
   /**
@@ -187,11 +206,12 @@ public class HostService extends BaseService {
   /**
   /**
    * Create a host resource definition.
    * Create a host resource definition.
    *
    *
-   * @param hostName    host name
-   * @param clusterName cluster name
+   * @param hostName     host name
+   * @param clusterName  cluster name
+   * @param ui           uri information
    * @return a host resource definition
    * @return a host resource definition
    */
    */
-  ResourceDefinition createResourceDefinition(String hostName, String clusterName) {
-    return new HostResourceDefinition(hostName, clusterName);
+  ResourceDefinition createResourceDefinition(String hostName, String clusterName, UriInfo ui) {
+    return new HostResourceDefinition(hostName, clusterName, ui.getRequestUri().toString().contains("/clusters/"));
   }
   }
 }
 }

+ 3 - 9
ambari-server/src/main/java/org/apache/ambari/server/api/services/PersistKeyValueService.java

@@ -21,14 +21,12 @@ package org.apache.ambari.server.api.services;
 import java.io.IOException;
 import java.io.IOException;
 import java.util.Map;
 import java.util.Map;
 
 
-import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.GET;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.Produces;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.JAXBException;
 
 
@@ -36,8 +34,6 @@ import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
 import org.apache.ambari.server.utils.StageUtils;
 import org.apache.ambari.server.utils.StageUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.commons.logging.LogFactory;
-import org.codehaus.jackson.JsonGenerationException;
-import org.codehaus.jackson.map.JsonMappingException;
 
 
 import com.google.inject.Inject;
 import com.google.inject.Inject;
 
 
@@ -53,10 +49,9 @@ public class PersistKeyValueService {
 
 
   @POST
   @POST
   @Produces("text/plain")
   @Produces("text/plain")
-  public Response update(String keyValues,
-      @Context HttpServletRequest req)
+  public Response update(String keyValues)
       throws WebApplicationException, InvalidStateTransitionException,
       throws WebApplicationException, InvalidStateTransitionException,
-      JsonGenerationException, JsonMappingException, JAXBException, IOException {
+      JAXBException, IOException {
     LOG.info("Received message from UI " + keyValues);
     LOG.info("Received message from UI " + keyValues);
     Map<String, String> keyValuesMap = StageUtils.fromJson(keyValues, Map.class);
     Map<String, String> keyValuesMap = StageUtils.fromJson(keyValues, Map.class);
     /* Call into the heartbeat handler */
     /* Call into the heartbeat handler */
@@ -77,8 +72,7 @@ public class PersistKeyValueService {
   
   
   @GET
   @GET
   @Produces("text/plain")
   @Produces("text/plain")
-  public String getAllKeyValues() throws JsonGenerationException,
-    JsonMappingException, JAXBException, IOException {
+  public String getAllKeyValues() throws JAXBException, IOException {
     Map<String, String> ret = persistKeyVal.getAllKeyValues();
     Map<String, String> ret = persistKeyVal.getAllKeyValues();
     String stringRet = StageUtils.jaxbToString(ret);
     String stringRet = StageUtils.jaxbToString(ret);
     LOG.info("Returning " + stringRet);
     LOG.info("Returning " + stringRet);

+ 8 - 2
ambari-server/src/main/java/org/apache/ambari/server/api/services/PersistenceManager.java

@@ -19,8 +19,12 @@
 package org.apache.ambari.server.api.services;
 package org.apache.ambari.server.api.services;
 
 
 import org.apache.ambari.server.api.resources.ResourceDefinition;
 import org.apache.ambari.server.api.resources.ResourceDefinition;
+import org.apache.ambari.server.controller.spi.PropertyId;
 import org.apache.ambari.server.controller.spi.RequestStatus;
 import org.apache.ambari.server.controller.spi.RequestStatus;
 
 
+import java.util.Map;
+import java.util.Set;
+
 /**
 /**
  * Persistence manager which is responsible for persisting a resource state to the back end.
  * Persistence manager which is responsible for persisting a resource state to the back end.
  * This includes create, update and delete operations.
  * This includes create, update and delete operations.
@@ -29,10 +33,12 @@ public interface PersistenceManager {
   /**
   /**
    * Persist a resource to the back end.
    * Persist a resource to the back end.
    *
    *
-   * @param resource  the resource to persist
+   *
+   * @param resource       resource definition for request
+   * @param setProperties  properties to be persisted.
    *
    *
    * @return the request state.
    * @return the request state.
    *
    *
    */
    */
-  public RequestStatus persist(ResourceDefinition resource);
+  public RequestStatus persist(ResourceDefinition resource, Set<Map<PropertyId, Object>> setProperties);
 }
 }

+ 3 - 2
ambari-server/src/main/java/org/apache/ambari/server/api/services/Request.java

@@ -26,6 +26,7 @@ import org.apache.ambari.server.controller.spi.TemporalInfo;
 
 
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
+import java.util.Set;
 
 
 /**
 /**
  * Provides information on the current request.
  * Provides information on the current request.
@@ -120,9 +121,9 @@ public interface Request {
   /**
   /**
    * Obtain the properties which have been parsed from the http body.
    * Obtain the properties which have been parsed from the http body.
    *
    *
-   * @return a map containing the properties contained in the http body
+   * @return a set of maps containing the properties contained in the http body
    */
    */
-  public Map<PropertyId, String> getHttpBodyProperties();
+  public Set<Map<PropertyId, Object>> getHttpBodyProperties();
 
 
     //TODO: refactor persistence mechanism
     //TODO: refactor persistence mechanism
   /**
   /**

+ 1 - 1
ambari-server/src/main/java/org/apache/ambari/server/api/services/RequestImpl.java

@@ -194,7 +194,7 @@ public class RequestImpl implements Request {
   }
   }
 
 
   @Override
   @Override
-  public Map<PropertyId, String> getHttpBodyProperties() {
+  public Set<Map<PropertyId, Object>> getHttpBodyProperties() {
     return getHttpBodyParser().parse(getHttpBody());
     return getHttpBodyParser().parse(getHttpBody());
   }
   }
 
 

+ 6 - 2
ambari-server/src/main/java/org/apache/ambari/server/api/services/UpdatePersistenceManager.java

@@ -21,18 +21,22 @@ package org.apache.ambari.server.api.services;
 
 
 import org.apache.ambari.server.api.resources.ResourceDefinition;
 import org.apache.ambari.server.api.resources.ResourceDefinition;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.controller.spi.PropertyId;
 import org.apache.ambari.server.controller.spi.RequestStatus;
 import org.apache.ambari.server.controller.spi.RequestStatus;
 
 
+import java.util.Map;
+import java.util.Set;
+
 
 
 /**
 /**
  * Responsible for persisting the updating of a resource in the back end.
  * Responsible for persisting the updating of a resource in the back end.
  */
  */
 public class UpdatePersistenceManager extends BasePersistenceManager {
 public class UpdatePersistenceManager extends BasePersistenceManager {
   @Override
   @Override
-  public RequestStatus persist(ResourceDefinition resource) {
+  public RequestStatus persist(ResourceDefinition resource, Set<Map<PropertyId, Object>> setProperties) {
     try {
     try {
       return getClusterController().updateResources(resource.getType(), createControllerRequest(
       return getClusterController().updateResources(resource.getType(), createControllerRequest(
-          resource.getProperties()), resource.getQuery().getInternalPredicate());
+          setProperties), resource.getQuery().getInternalPredicate());
     } catch (AmbariException e) {
     } catch (AmbariException e) {
       //todo: handle exception
       //todo: handle exception
       throw new RuntimeException("Update of resource failed: " + e, e);
       throw new RuntimeException("Update of resource failed: " + e, e);

+ 20 - 11
ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/JsonPropertyParser.java

@@ -24,42 +24,51 @@ import org.codehaus.jackson.JsonNode;
 import org.codehaus.jackson.map.ObjectMapper;
 import org.codehaus.jackson.map.ObjectMapper;
 
 
 import java.io.IOException;
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
+import java.util.*;
 
 
 /**
 /**
  * JSON parser which parses a JSON string into a map of properties and values.
  * JSON parser which parses a JSON string into a map of properties and values.
  */
  */
 public class JsonPropertyParser implements RequestBodyParser {
 public class JsonPropertyParser implements RequestBodyParser {
-  private Map<PropertyId, String> m_properties = new HashMap<PropertyId, String>();
+  private Set<Map<PropertyId, Object>> m_setProperties = new HashSet<Map<PropertyId, Object>>();
+
 
 
   @Override
   @Override
-  public Map<PropertyId, String> parse(String s) {
+  public Set<Map<PropertyId, Object>> parse(String s) {
+
     ObjectMapper mapper = new ObjectMapper();
     ObjectMapper mapper = new ObjectMapper();
 
 
     if (s != null && ! s.isEmpty()) {
     if (s != null && ! s.isEmpty()) {
+      s = ensureArrayFormat(s);
       try {
       try {
-        processNode(mapper.readValue(s, JsonNode.class), "");
+        JsonNode[] nodes = mapper.readValue(s, JsonNode[].class);
+        for(JsonNode node : nodes) {
+          Map<PropertyId, Object> mapProperties = new HashMap<PropertyId, Object>();
+          processNode(node, "", mapProperties);
+          m_setProperties.add(mapProperties);
+        }
       } catch (IOException e) {
       } catch (IOException e) {
         throw new RuntimeException("Unable to parse json: " + e, e);
         throw new RuntimeException("Unable to parse json: " + e, e);
       }
       }
     }
     }
-
-    return m_properties;
+    return m_setProperties;
   }
   }
 
 
-  private void processNode(JsonNode node, String path) {
+  private void processNode(JsonNode node, String path, Map<PropertyId, Object> mapProperties) {
     Iterator<String> iter = node.getFieldNames();
     Iterator<String> iter = node.getFieldNames();
     String name;
     String name;
     while (iter.hasNext()) {
     while (iter.hasNext()) {
       name = iter.next();
       name = iter.next();
       JsonNode child = node.get(name);
       JsonNode child = node.get(name);
       if (child.isContainerNode()) {
       if (child.isContainerNode()) {
-        processNode(child, path.isEmpty() ? name : path + '.' + name);
+        processNode(child, path.isEmpty() ? name : path + '.' + name, mapProperties);
       } else {
       } else {
-        m_properties.put(PropertyHelper.getPropertyId(name, path), child.asText());
+        mapProperties.put(PropertyHelper.getPropertyId(name, path), child.asText());
       }
       }
     }
     }
   }
   }
+
+  private String ensureArrayFormat(String s) {
+    return s.startsWith("[") ? s : '[' + s + ']';
+  }
 }
 }

+ 3 - 2
ambari-server/src/main/java/org/apache/ambari/server/api/services/parsers/RequestBodyParser.java

@@ -21,6 +21,7 @@ package org.apache.ambari.server.api.services.parsers;
 import org.apache.ambari.server.controller.spi.PropertyId;
 import org.apache.ambari.server.controller.spi.PropertyId;
 
 
 import java.util.Map;
 import java.util.Map;
+import java.util.Set;
 
 
 /**
 /**
  * Parse the provided String into a map of properties and associated values.
  * Parse the provided String into a map of properties and associated values.
@@ -32,7 +33,7 @@ public interface RequestBodyParser {
    *
    *
    * @param s  the string body to be parsed
    * @param s  the string body to be parsed
    *
    *
-   * @return a map of properties or an empty map if no properties exist
+   * @return a set of maps of properties or an empty set if no properties exist
    */
    */
-  public Map<PropertyId, String> parse(String s);
+  public Set<Map<PropertyId, Object>> parse(String s);
 }
 }

+ 2 - 0
ambari-server/src/main/python/ambari-server.py

@@ -41,6 +41,8 @@ IP_TBLS_ENABLED="Firewall is running"
 IP_TBLS_DISABLED="Firewall is not running"
 IP_TBLS_DISABLED="Firewall is not running"
 IP_TBLS_SRVC_NT_FND="iptables: unrecognized service"
 IP_TBLS_SRVC_NT_FND="iptables: unrecognized service"
 SERVER_START_CMD="{0}" + os.sep + "bin" + os.sep + "java -cp {1}"+ os.pathsep + ".." + os.sep + "lib" + os.sep + "ambari-server" + os.sep + "* org.apache.ambari.server.controller.AmbariServer"
 SERVER_START_CMD="{0}" + os.sep + "bin" + os.sep + "java -cp {1}"+ os.pathsep + ".." + os.sep + "lib" + os.sep + "ambari-server" + os.sep + "* org.apache.ambari.server.controller.AmbariServer"
+# uncomment for debug
+# SERVER_START_CMD="{0}" + os.sep + "bin" + os.sep + "java -Xdebug -Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n -cp {1}"+ os.pathsep + ".." + os.sep + "lib" + os.sep + "ambari-server" + os.sep + "* org.apache.ambari.server.controller.AmbariServer"
 AMBARI_CONF_VAR="AMBARI_CONF_DIR"
 AMBARI_CONF_VAR="AMBARI_CONF_DIR"
 PG_ST_CMD = "service postgresql status"
 PG_ST_CMD = "service postgresql status"
 PG_START_CMD = "service postgresql start"
 PG_START_CMD = "service postgresql start"

+ 26 - 17
ambari-server/src/main/python/bootstrap.py

@@ -64,10 +64,10 @@ class SCP(threading.Thread):
 
 
 class SSH(threading.Thread):
 class SSH(threading.Thread):
   """ Ssh implementation of this """
   """ Ssh implementation of this """
-  def __init__(self, sshKeyFile, host, commands):
+  def __init__(self, sshKeyFile, host, command):
     self.sshKeyFile = sshKeyFile
     self.sshKeyFile = sshKeyFile
     self.host = host
     self.host = host
-    self.commands = commands
+    self.command = command
     self.ret = {"exitstatus" : -1, "log": "FAILED"}
     self.ret = {"exitstatus" : -1, "log": "FAILED"}
     threading.Thread.__init__(self)
     threading.Thread.__init__(self)
     pass
     pass
@@ -81,7 +81,7 @@ class SSH(threading.Thread):
   def run(self):
   def run(self):
     sshcommand = ["ssh", "-o", "ConnectTimeOut=3", "-o",
     sshcommand = ["ssh", "-o", "ConnectTimeOut=3", "-o",
                    "StrictHostKeyChecking=no", "-i", self.sshKeyFile,
                    "StrictHostKeyChecking=no", "-i", self.sshKeyFile,
-                    self.host, ";".join(self.commands)]
+                    self.host, self.command]
     sshstat = subprocess.Popen(sshcommand, stdout=subprocess.PIPE,
     sshstat = subprocess.Popen(sshcommand, stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
                                   stderr=subprocess.PIPE)
     log = sshstat.communicate()
     log = sshstat.communicate()
@@ -96,10 +96,10 @@ def splitlist(hosts, n):
 
 
 class PSSH:
 class PSSH:
   """Run SSH in parallel for a given list of hosts"""
   """Run SSH in parallel for a given list of hosts"""
-  def __init__(self, hosts, sshKeyFile, commands):
+  def __init__(self, hosts, sshKeyFile, command):
     self.hosts = hosts
     self.hosts = hosts
     self.sshKeyFile = sshKeyFile
     self.sshKeyFile = sshKeyFile
-    self.commands = commands
+    self.command = command
     self.ret = {}
     self.ret = {}
     pass
     pass
     
     
@@ -112,7 +112,7 @@ class PSSH:
     for chunk in splitlist(self.hosts, 20):
     for chunk in splitlist(self.hosts, 20):
       chunkstats = []
       chunkstats = []
       for host in chunk:
       for host in chunk:
-        ssh = SSH(self.sshKeyFile, host, self.commands)
+        ssh = SSH(self.sshKeyFile, host, self.command)
         ssh.start()
         ssh.start()
         chunkstats.append(ssh)
         chunkstats.append(ssh)
         pass
         pass
@@ -173,32 +173,41 @@ class BootStrap:
     return os.path.join(self.scriptDir, "setupAgent.py")
     return os.path.join(self.scriptDir, "setupAgent.py")
     
     
   def runSetupAgent(self):
   def runSetupAgent(self):
-    commands = ["export AMBARI_PASSPHRASE=" + os.environ[AMBARI_PASSPHRASE_VAR], "/tmp/setupAgent.py"]
-    pssh = PSSH(self.hostlist, self.sshkeyFile, commands)
+    command = "python /tmp/setupAgent.py " + os.environ[AMBARI_PASSPHRASE_VAR]
+    pssh = PSSH(self.hostlist, self.sshkeyFile, command)
     pssh.run()
     pssh.run()
     out = pssh.getstatus()
     out = pssh.getstatus()
     logging.info("Parallel ssh returns " + pprint.pformat(out))
     logging.info("Parallel ssh returns " + pprint.pformat(out))
-
-    """ Test code for setting env var on agent host before starting setupAgent.py
-    commands = ["export AMBARI_PASSPHRASE=" + os.environ[AMBARI_PASSPHRASE_VAR], "set"]
-    pssh = PSSH(self.hostlist, self.sshkeyFile, commands)
-    pssh.run()
-    out = pssh.getstatus()
-    logging.info("Look for AMBARI_PASSPHRASE in out " + pprint.pformat(out))
-    """
+    pass
 
 
   def copyNeededFiles(self):
   def copyNeededFiles(self):
     try:
     try:
       """Copying the files """
       """Copying the files """
+      """ Uncomment when ambari.repo is ready
       fileToCopy = self.getRepoFile()
       fileToCopy = self.getRepoFile()
       pscp = PSCP(self.hostlist, self.sshkeyFile, fileToCopy, "/etc/yum.repos.d")
       pscp = PSCP(self.hostlist, self.sshkeyFile, fileToCopy, "/etc/yum.repos.d")
       pscp.run()
       pscp.run()
       out = pscp.getstatus()
       out = pscp.getstatus()
       logging.info("Parallel scp return " + pprint.pformat(out))
       logging.info("Parallel scp return " + pprint.pformat(out))
+      """
+
+      """ Remove this block when ambari.repo is ready """
+      """ copy agent rpm to remote host """
+      pscp = PSCP(self.hostlist, self.sshkeyFile, "/home/centos/ambari/ambari-agent/target/rpm/ambari-agent/RPMS/noarch/ambari-agent*.rpm", "/tmp")
+      pscp.run()
+      out = pscp.getstatus()
+      logging.info("Parallel scp return " + pprint.pformat(out))
+
+
+      pscp = PSCP(self.hostlist, self.sshkeyFile, "/usr/lib/python2.6/site-packages/ambari_server/setupAgent.py", "/tmp")
+      pscp.run()
+      out = pscp.getstatus()
+      logging.info("Parallel scp return " + pprint.pformat(out))
+
     except Exception as e:
     except Exception as e:
       logging.info("Traceback " + traceback.format_exc())
       logging.info("Traceback " + traceback.format_exc())
       pass
       pass
-       
+
     pass
     pass
   
   
   def run(self):
   def run(self):

+ 38 - 7
ambari-server/src/main/python/setupAgent.py

@@ -29,26 +29,57 @@ import threading
 import traceback
 import traceback
 from pprint import pformat
 from pprint import pformat
 
 
-def installAgent():
-  """ Run yum install and make sure the agent install alright """
-  # TODO replace echo with yum
-  yumcommand = ["echo", "install", "ambari-agent"]
-  yumstat = subprocess.Popen(yumcommand, stdout=subprocess.PIPE)
-  log = yumstat.communicate(0)
+AMBARI_PASSPHRASE_VAR = "AMBARI_PASSPHRASE"
+
+def execOsCommand(osCommand):
+  """ Run yum install and make sure the puppet install alright """
+  osStat = subprocess.Popen(osCommand, stdout=subprocess.PIPE)
+  log = osStat.communicate(0)
   ret = {}
   ret = {}
-  ret["exitstatus"] = yumstat.returncode
+  ret["exitstatus"] = osStat.returncode
   ret["log"] = log
   ret["log"] = log
   return ret
   return ret
 
 
+def installPreReq():
+  """ Adds hdp repo
+  rpmCommand = ["rpm", "-Uvh", "http://public-repo-1.hortonworks.com/HDP-1.1.1.16/repos/centos6/hdp-release-1.1.1.16-1.el6.noarch.rpm"]
+  execOsCommand(rpmCommand)
+  """
+  yumCommand = ["yum", "-y", "install", "epel-release"]
+  execOsCommand(yumCommand)
+
+def installPuppet():
+  """ Run yum install and make sure the puppet install alright """
+  osCommand = ["useradd", "-G", "puppet", "puppet"]
+  execOsCommand(osCommand)
+  yumCommand = ["yum", "-y", "install", "puppet"]
+  return execOsCommand(yumCommand)
+
+def installAgent():
+  """ Run yum install and make sure the agent install alright """
+  # TODO replace rpm with yum -y
+  rpmCommand = ["yum", "install", "-y", "/tmp/ambari-agent*.rpm"]
+  return execOsCommand(rpmCommand)
+
 def configureAgent():
 def configureAgent():
   """ Configure the agent so that it has all the configs knobs properly 
   """ Configure the agent so that it has all the configs knobs properly 
   installed """
   installed """
   return
   return
 
 
+def runAgent(passPhrase):
+  os.environ[AMBARI_PASSPHRASE_VAR] = passPhrase
+  subprocess.call("/usr/sbin/ambari-agent start", shell=True)
+
 def main(argv=None):
 def main(argv=None):
   scriptDir = os.path.realpath(os.path.dirname(argv[0]))
   scriptDir = os.path.realpath(os.path.dirname(argv[0]))
+  """ Parse the input"""
+  onlyargs = argv[1:]
+  passPhrase = onlyargs[0]
+  installPreReq()
+  # installPuppet()
   installAgent()
   installAgent()
   configureAgent()
   configureAgent()
+  runAgent(passPhrase)
   
   
 if __name__ == '__main__':
 if __name__ == '__main__':
   logging.basicConfig(level=logging.DEBUG)
   logging.basicConfig(level=logging.DEBUG)

+ 1 - 1
ambari-server/src/main/resources/ca.config

@@ -1,7 +1,7 @@
 [ ca ]
 [ ca ]
 default_ca             = CA_CLIENT
 default_ca             = CA_CLIENT
 [ CA_CLIENT ]
 [ CA_CLIENT ]
-dir		       = keystore/db
+dir		       = /var/lib/ambari-server/keys/db
 certs                  = $dir/certs
 certs                  = $dir/certs
 new_certs_dir          = $dir/newcerts
 new_certs_dir          = $dir/newcerts
 
 

+ 6 - 8
ambari-server/src/test/java/org/apache/ambari/server/api/handlers/CreateHandlerTest.java

@@ -48,7 +48,7 @@ public class CreateHandlerTest {
     Resource resource1 = createMock(Resource.class);
     Resource resource1 = createMock(Resource.class);
     Resource resource2 = createMock(Resource.class);
     Resource resource2 = createMock(Resource.class);
 
 
-    Map<PropertyId, String> resourceProperties = new HashMap<PropertyId, String>();
+    Set<Map<PropertyId, Object>> setResourceProperties = new HashSet<Map<PropertyId, Object>>();
 
 
     Set<Resource> setResources = new HashSet<Resource>();
     Set<Resource> setResources = new HashSet<Resource>();
     setResources.add(resource1);
     setResources.add(resource1);
@@ -56,10 +56,9 @@ public class CreateHandlerTest {
 
 
     // expectations
     // expectations
     expect(request.getResourceDefinition()).andReturn(resource);
     expect(request.getResourceDefinition()).andReturn(resource);
-    expect(request.getHttpBodyProperties()).andReturn(resourceProperties);
-    resource.setProperties(resourceProperties);
+    expect(request.getHttpBodyProperties()).andReturn(setResourceProperties);
     expect(request.getPersistenceManager()).andReturn(pm);
     expect(request.getPersistenceManager()).andReturn(pm);
-    expect(pm.persist(resource)).andReturn(status);
+    expect(pm.persist(resource, setResourceProperties)).andReturn(status);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Complete);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Complete);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
@@ -100,7 +99,7 @@ public class CreateHandlerTest {
     Resource resource2 = createMock(Resource.class);
     Resource resource2 = createMock(Resource.class);
     Resource requestResource = createMock(Resource.class);
     Resource requestResource = createMock(Resource.class);
 
 
-    Map<PropertyId, String> resourceProperties = new HashMap<PropertyId, String>();
+    Set<Map<PropertyId, Object>> setResourceProperties = new HashSet<Map<PropertyId, Object>>();
 
 
     Set<Resource> setResources = new HashSet<Resource>();
     Set<Resource> setResources = new HashSet<Resource>();
     setResources.add(resource1);
     setResources.add(resource1);
@@ -108,10 +107,9 @@ public class CreateHandlerTest {
 
 
     // expectations
     // expectations
     expect(request.getResourceDefinition()).andReturn(resource);
     expect(request.getResourceDefinition()).andReturn(resource);
-    expect(request.getHttpBodyProperties()).andReturn(resourceProperties);
-    resource.setProperties(resourceProperties);
+    expect(request.getHttpBodyProperties()).andReturn(setResourceProperties);
     expect(request.getPersistenceManager()).andReturn(pm);
     expect(request.getPersistenceManager()).andReturn(pm);
-    expect(pm.persist(resource)).andReturn(status);
+    expect(pm.persist(resource, setResourceProperties)).andReturn(status);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Accepted);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Accepted);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();

+ 7 - 12
ambari-server/src/test/java/org/apache/ambari/server/api/handlers/DeleteHandlerTest.java

@@ -29,10 +29,7 @@ import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.junit.Test;
 import org.junit.Test;
 
 
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 
 import static org.easymock.EasyMock.*;
 import static org.easymock.EasyMock.*;
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
@@ -51,7 +48,7 @@ public class DeleteHandlerTest {
     Resource resource1 = createMock(Resource.class);
     Resource resource1 = createMock(Resource.class);
     Resource resource2 = createMock(Resource.class);
     Resource resource2 = createMock(Resource.class);
 
 
-    Map<PropertyId, String> resourceProperties = new HashMap<PropertyId, String>();
+    Set<Map<PropertyId, Object>> setResourceProperties = new HashSet<Map<PropertyId, Object>>();
 
 
     Set<Resource> setResources = new HashSet<Resource>();
     Set<Resource> setResources = new HashSet<Resource>();
     setResources.add(resource1);
     setResources.add(resource1);
@@ -59,10 +56,9 @@ public class DeleteHandlerTest {
 
 
     // expectations
     // expectations
     expect(request.getResourceDefinition()).andReturn(resource);
     expect(request.getResourceDefinition()).andReturn(resource);
-    expect(request.getHttpBodyProperties()).andReturn(resourceProperties);
-    resource.setProperties(resourceProperties);
+    expect(request.getHttpBodyProperties()).andReturn(setResourceProperties);
     expect(request.getPersistenceManager()).andReturn(pm);
     expect(request.getPersistenceManager()).andReturn(pm);
-    expect(pm.persist(resource)).andReturn(status);
+    expect(pm.persist(resource, setResourceProperties)).andReturn(status);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Complete);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Complete);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
@@ -103,7 +99,7 @@ public class DeleteHandlerTest {
     Resource resource2 = createMock(Resource.class);
     Resource resource2 = createMock(Resource.class);
     Resource requestResource = createMock(Resource.class);
     Resource requestResource = createMock(Resource.class);
 
 
-    Map<PropertyId, String> resourceProperties = new HashMap<PropertyId, String>();
+    Set<Map<PropertyId, Object>> setResourceProperties = new HashSet<Map<PropertyId, Object>>();
 
 
     Set<Resource> setResources = new HashSet<Resource>();
     Set<Resource> setResources = new HashSet<Resource>();
     setResources.add(resource1);
     setResources.add(resource1);
@@ -111,10 +107,9 @@ public class DeleteHandlerTest {
 
 
     // expectations
     // expectations
     expect(request.getResourceDefinition()).andReturn(resource);
     expect(request.getResourceDefinition()).andReturn(resource);
-    expect(request.getHttpBodyProperties()).andReturn(resourceProperties);
-    resource.setProperties(resourceProperties);
+    expect(request.getHttpBodyProperties()).andReturn(setResourceProperties);
     expect(request.getPersistenceManager()).andReturn(pm);
     expect(request.getPersistenceManager()).andReturn(pm);
-    expect(pm.persist(resource)).andReturn(status);
+    expect(pm.persist(resource, setResourceProperties)).andReturn(status);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Accepted);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Accepted);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();

+ 7 - 12
ambari-server/src/test/java/org/apache/ambari/server/api/handlers/UpdateHandlerTest.java

@@ -29,10 +29,7 @@ import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.junit.Test;
 import org.junit.Test;
 
 
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 
 import static org.easymock.EasyMock.*;
 import static org.easymock.EasyMock.*;
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
@@ -51,7 +48,7 @@ public class UpdateHandlerTest {
     Resource resource1 = createMock(Resource.class);
     Resource resource1 = createMock(Resource.class);
     Resource resource2 = createMock(Resource.class);
     Resource resource2 = createMock(Resource.class);
 
 
-    Map<PropertyId, String> resourceProperties = new HashMap<PropertyId, String>();
+    Set<Map<PropertyId, Object>> setResourceProperties = new HashSet<Map<PropertyId, Object>>();
 
 
     Set<Resource> setResources = new HashSet<Resource>();
     Set<Resource> setResources = new HashSet<Resource>();
     setResources.add(resource1);
     setResources.add(resource1);
@@ -59,10 +56,9 @@ public class UpdateHandlerTest {
 
 
     // expectations
     // expectations
     expect(request.getResourceDefinition()).andReturn(resource);
     expect(request.getResourceDefinition()).andReturn(resource);
-    expect(request.getHttpBodyProperties()).andReturn(resourceProperties);
-    resource.setProperties(resourceProperties);
+    expect(request.getHttpBodyProperties()).andReturn(setResourceProperties);
     expect(request.getPersistenceManager()).andReturn(pm);
     expect(request.getPersistenceManager()).andReturn(pm);
-    expect(pm.persist(resource)).andReturn(status);
+    expect(pm.persist(resource, setResourceProperties)).andReturn(status);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Complete);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Complete);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
@@ -103,7 +99,7 @@ public class UpdateHandlerTest {
     Resource resource2 = createMock(Resource.class);
     Resource resource2 = createMock(Resource.class);
     Resource requestResource = createMock(Resource.class);
     Resource requestResource = createMock(Resource.class);
 
 
-    Map<PropertyId, String> resourceProperties = new HashMap<PropertyId, String>();
+    Set<Map<PropertyId, Object>> setResourceProperties = new HashSet<Map<PropertyId, Object>>();
 
 
     Set<Resource> setResources = new HashSet<Resource>();
     Set<Resource> setResources = new HashSet<Resource>();
     setResources.add(resource1);
     setResources.add(resource1);
@@ -111,10 +107,9 @@ public class UpdateHandlerTest {
 
 
     // expectations
     // expectations
     expect(request.getResourceDefinition()).andReturn(resource);
     expect(request.getResourceDefinition()).andReturn(resource);
-    expect(request.getHttpBodyProperties()).andReturn(resourceProperties);
-    resource.setProperties(resourceProperties);
+    expect(request.getHttpBodyProperties()).andReturn(setResourceProperties);
     expect(request.getPersistenceManager()).andReturn(pm);
     expect(request.getPersistenceManager()).andReturn(pm);
-    expect(pm.persist(resource)).andReturn(status);
+    expect(pm.persist(resource, setResourceProperties)).andReturn(status);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Accepted);
     expect(status.getStatus()).andReturn(RequestStatus.Status.Accepted);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(status.getAssociatedResources()).andReturn(setResources);
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();
     expect(resource1.getType()).andReturn(Resource.Type.Cluster).anyTimes();

+ 1 - 6
ambari-server/src/test/java/org/apache/ambari/server/api/query/QueryImplTest.java

@@ -71,13 +71,8 @@ public class QueryImplTest {
         eq(predicate))).andReturn(listResources);
         eq(predicate))).andReturn(listResources);
 
 
     expect(result.getResultTree()).andReturn(tree);
     expect(result.getResultTree()).andReturn(tree);
-    expect(componentResource.getPropertyValue(componentPropertyId)).andReturn("componentName");
-    hostComponentResourceDef.setParentId(Resource.Type.Component, "componentName");
 
 
-    //todo: debug output
-    expect(hostComponentResourceDef.getId()).andReturn(null).anyTimes();
-    expect(hostComponentResourceDef.getResourceIds()).andReturn(mapResourceIds).anyTimes();
-    // end todo
+    hostComponentResourceDef.setParentIds(mapResourceIds);
 
 
     expect(hostComponentResourceDef.getQuery()).andReturn(hostComponentQuery);
     expect(hostComponentResourceDef.getQuery()).andReturn(hostComponentQuery);
     expect(hostComponentQuery.execute()).andReturn(hostComponentQueryResult);
     expect(hostComponentQuery.execute()).andReturn(hostComponentQueryResult);

+ 1 - 1
ambari-server/src/test/java/org/apache/ambari/server/api/services/ConfigurationServiceTest.java

@@ -96,7 +96,7 @@ public class ConfigurationServiceTest {
     
     
 
 
     @Override
     @Override
-    ResourceDefinition createResourceDefinition(String type, String tag, String clusterName) {
+    ResourceDefinition createResourceDefinition(String type, String clusterName) {
       assertEquals(m_clusterId, clusterName);
       assertEquals(m_clusterId, clusterName);
       return m_resourceDef;
       return m_resourceDef;
     }
     }

+ 59 - 13
ambari-server/src/test/java/org/apache/ambari/server/api/services/CreatePersistenceManagerTest.java

@@ -25,10 +25,13 @@ import org.apache.ambari.server.controller.spi.ClusterController;
 import org.apache.ambari.server.controller.spi.PropertyId;
 import org.apache.ambari.server.controller.spi.PropertyId;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.spi.Schema;
 import org.apache.ambari.server.controller.spi.Schema;
+import org.apache.ambari.server.controller.spi.Request;
 import org.junit.Test;
 import org.junit.Test;
 
 
 import java.util.HashMap;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Map;
+import java.util.Set;
 
 
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
 
 
@@ -45,33 +48,76 @@ public class CreatePersistenceManagerTest {
     Schema schema = createMock(Schema.class);
     Schema schema = createMock(Schema.class);
     PropertyId clusterId = createStrictMock(PropertyId.class);
     PropertyId clusterId = createStrictMock(PropertyId.class);
     PropertyId serviceId = createStrictMock(PropertyId.class);
     PropertyId serviceId = createStrictMock(PropertyId.class);
-    org.apache.ambari.server.controller.spi.Request serverRequest =
-        createStrictMock(org.apache.ambari.server.controller.spi.Request.class);
+    Request serverRequest = createStrictMock(Request.class);
 
 
     Map<Resource.Type, String> mapResourceIds = new HashMap<Resource.Type, String>();
     Map<Resource.Type, String> mapResourceIds = new HashMap<Resource.Type, String>();
     mapResourceIds.put(Resource.Type.Cluster, "clusterId");
     mapResourceIds.put(Resource.Type.Cluster, "clusterId");
     mapResourceIds.put(Resource.Type.Service, "serviceId");
     mapResourceIds.put(Resource.Type.Service, "serviceId");
 
 
+    Set<Map<PropertyId, Object>> setProperties = new HashSet<Map<PropertyId, Object>>();
     Map<PropertyId, Object> mapProperties = new HashMap<PropertyId, Object>();
     Map<PropertyId, Object> mapProperties = new HashMap<PropertyId, Object>();
     mapProperties.put(clusterId, "clusterId");
     mapProperties.put(clusterId, "clusterId");
     mapProperties.put(serviceId, "serviceId");
     mapProperties.put(serviceId, "serviceId");
     mapProperties.put(PropertyHelper.getPropertyId("bar", "foo"), "value");
     mapProperties.put(PropertyHelper.getPropertyId("bar", "foo"), "value");
+    setProperties.add(mapProperties);
 
 
     //expectations
     //expectations
     expect(resource.getResourceIds()).andReturn(mapResourceIds);
     expect(resource.getResourceIds()).andReturn(mapResourceIds);
     expect(resource.getType()).andReturn(Resource.Type.Component);
     expect(resource.getType()).andReturn(Resource.Type.Component);
     expect(controller.getSchema(Resource.Type.Component)).andReturn(schema);
     expect(controller.getSchema(Resource.Type.Component)).andReturn(schema);
     expect(schema.getKeyPropertyId(Resource.Type.Cluster)).andReturn(clusterId);
     expect(schema.getKeyPropertyId(Resource.Type.Cluster)).andReturn(clusterId);
-    resource.setProperty(clusterId, "clusterId");
     expect(schema.getKeyPropertyId(Resource.Type.Service)).andReturn(serviceId);
     expect(schema.getKeyPropertyId(Resource.Type.Service)).andReturn(serviceId);
-    resource.setProperty(serviceId, "serviceId");
-    expect(resource.getProperties()).andReturn(mapProperties);
 
 
     expect(controller.createResources(Resource.Type.Component, serverRequest)).andReturn(new RequestStatusImpl(null));
     expect(controller.createResources(Resource.Type.Component, serverRequest)).andReturn(new RequestStatusImpl(null));
 
 
     replay(resource, controller, schema, clusterId, serviceId, serverRequest);
     replay(resource, controller, schema, clusterId, serviceId, serverRequest);
 
 
-    new TestCreatePersistenceManager(controller, mapProperties, serverRequest).persist(resource);
+    new TestCreatePersistenceManager(controller, setProperties, serverRequest).persist(resource, setProperties);
+
+    verify(resource, controller, schema, clusterId, serviceId, serverRequest);
+
+  }
+
+  @Test
+  public void testPersist__MultipleResources() throws Exception {
+    ResourceDefinition resource = createMock(ResourceDefinition.class);
+    ClusterController controller = createMock(ClusterController.class);
+    Schema schema = createMock(Schema.class);
+    PropertyId clusterId = createStrictMock(PropertyId.class);
+    PropertyId serviceId = createStrictMock(PropertyId.class);
+    Request serverRequest = createStrictMock(Request.class);
+
+    Map<Resource.Type, String> mapResourceIds = new HashMap<Resource.Type, String>();
+    mapResourceIds.put(Resource.Type.Cluster, "clusterId");
+    mapResourceIds.put(Resource.Type.Service, "serviceId");
+
+    Set<Map<PropertyId, Object>> setProperties = new HashSet<Map<PropertyId, Object>>();
+
+    Map<PropertyId, Object> mapResourceProps1 = new HashMap<PropertyId, Object>();
+    mapResourceProps1.put(clusterId, "clusterId");
+    mapResourceProps1.put(serviceId, "serviceId");
+    mapResourceProps1.put(PropertyHelper.getPropertyId("bar", "foo"), "value");
+
+    Map<PropertyId, Object> mapResourceProps2 = new HashMap<PropertyId, Object>();
+    mapResourceProps2.put(clusterId, "clusterId");
+    mapResourceProps2.put(serviceId, "serviceId2");
+    mapResourceProps2.put(PropertyHelper.getPropertyId("bar2", "foo"), "value2");
+
+    setProperties.add(mapResourceProps1);
+    setProperties.add(mapResourceProps2);
+
+    //expectations
+    expect(resource.getResourceIds()).andReturn(mapResourceIds);
+    expect(resource.getType()).andReturn(Resource.Type.Component);
+    expect(controller.getSchema(Resource.Type.Component)).andReturn(schema);
+    expect(schema.getKeyPropertyId(Resource.Type.Cluster)).andReturn(clusterId).times(2);
+    expect(schema.getKeyPropertyId(Resource.Type.Service)).andReturn(serviceId).times(2);
+
+    expect(controller.createResources(Resource.Type.Component, serverRequest)).andReturn(new RequestStatusImpl(null));
+
+    replay(resource, controller, schema, clusterId, serviceId, serverRequest);
+
+    new TestCreatePersistenceManager(controller, setProperties, serverRequest).persist(resource, setProperties);
 
 
     verify(resource, controller, schema, clusterId, serviceId, serverRequest);
     verify(resource, controller, schema, clusterId, serviceId, serverRequest);
 
 
@@ -80,14 +126,14 @@ public class CreatePersistenceManagerTest {
   private class TestCreatePersistenceManager extends CreatePersistenceManager {
   private class TestCreatePersistenceManager extends CreatePersistenceManager {
 
 
     private ClusterController m_controller;
     private ClusterController m_controller;
-    private org.apache.ambari.server.controller.spi.Request m_request;
-    private Map<PropertyId, Object> m_mapProperties;
+    private Request m_request;
+    private Set<Map<PropertyId, Object>> m_setProperties;
 
 
     private TestCreatePersistenceManager(ClusterController controller,
     private TestCreatePersistenceManager(ClusterController controller,
-                                         Map<PropertyId, Object> mapProperties,
-                                         org.apache.ambari.server.controller.spi.Request controllerRequest) {
+                                         Set<Map<PropertyId, Object>> setProperties,
+                                         Request controllerRequest) {
       m_controller = controller;
       m_controller = controller;
-      m_mapProperties = mapProperties;
+      m_setProperties = setProperties;
       m_request = controllerRequest;
       m_request = controllerRequest;
     }
     }
 
 
@@ -97,8 +143,8 @@ public class CreatePersistenceManagerTest {
     }
     }
 
 
     @Override
     @Override
-    protected org.apache.ambari.server.controller.spi.Request createControllerRequest(Map<PropertyId, Object> properties) {
-      assertEquals(m_mapProperties, properties);
+    protected Request createControllerRequest(Set<Map<PropertyId, Object>> setProperties) {
+      assertEquals(m_setProperties, setProperties);
       return m_request;
       return m_request;
     }
     }
   }
   }

+ 1 - 1
ambari-server/src/test/java/org/apache/ambari/server/api/services/DeletePersistenceManagerTest.java

@@ -49,7 +49,7 @@ public class DeletePersistenceManagerTest {
 
 
     replay(resource, controller, schema, query, predicate);
     replay(resource, controller, schema, query, predicate);
 
 
-    new TestDeletePersistenceManager(controller).persist(resource);
+    new TestDeletePersistenceManager(controller).persist(resource, null);
 
 
     verify(resource, controller, schema, query, predicate);
     verify(resource, controller, schema, query, predicate);
   }
   }

+ 57 - 3
ambari-server/src/test/java/org/apache/ambari/server/api/services/HostServiceTest.java

@@ -134,7 +134,7 @@ public class HostServiceTest {
     String hostName = "hostName";
     String hostName = "hostName";
 
 
     // expectations
     // expectations
-    expect(requestFactory.createRequest(eq(httpHeaders), eq("body"), eq(uriInfo), eq(Request.Type.POST),
+    expect(requestFactory.createRequest(eq(httpHeaders), isNull(String.class), eq(uriInfo), eq(Request.Type.POST),
         eq(resourceDef))).andReturn(request);
         eq(resourceDef))).andReturn(request);
 
 
     expect(requestHandler.handleRequest(request)).andReturn(result);
     expect(requestHandler.handleRequest(request)).andReturn(result);
@@ -148,7 +148,61 @@ public class HostServiceTest {
 
 
     //test
     //test
     HostService hostService = new TestHostService(resourceDef, clusterName, hostName, requestFactory, responseFactory, requestHandler);
     HostService hostService = new TestHostService(resourceDef, clusterName, hostName, requestFactory, responseFactory, requestHandler);
-    assertSame(response, hostService.createHost("body", httpHeaders, uriInfo, hostName));
+    assertSame(response, hostService.createHost(null, httpHeaders, uriInfo, hostName));
+
+    verify(resourceDef, resultSerializer, requestFactory, responseFactory, request, requestHandler,
+        result, response, httpHeaders, uriInfo);
+  }
+
+  @Test
+  public void testCreateHosts() {
+    ResourceDefinition resourceDef = createStrictMock(ResourceDefinition.class);
+    ResultSerializer resultSerializer = createStrictMock(ResultSerializer.class);
+    Object serializedResult = new Object();
+    RequestFactory requestFactory = createStrictMock(RequestFactory.class);
+    ResponseFactory responseFactory = createStrictMock(ResponseFactory.class);
+    Request request = createNiceMock(Request.class);
+    RequestHandler requestHandler = createStrictMock(RequestHandler.class);
+    Result result = createStrictMock(Result.class);
+    Response response = createStrictMock(Response.class);
+
+    HttpHeaders httpHeaders = createNiceMock(HttpHeaders.class);
+    UriInfo uriInfo = createNiceMock(UriInfo.class);
+
+    String clusterName = "clusterName";
+    String body = "[ " +
+        "{\"Hosts\" : {" +
+        "            \"cluster_name\" : \"mycluster\"," +
+        "            \"host_name\" : \"host1\"" +
+        "          }" +
+        "}," +
+        "{\"Hosts\" : {" +
+        "            \"cluster_name\" : \"mycluster\"," +
+        "            \"host_name\" : \"host2\"" +
+        "          }" +
+        "}," +
+        "{\"Hosts\" : {" +
+        "            \"cluster_name\" : \"mycluster\"," +
+        "            \"host_name\" : \"host3\"" +
+        "          }" +
+        "}]";
+
+    // expectations
+    expect(requestFactory.createRequest(eq(httpHeaders), eq(body), eq(uriInfo), eq(Request.Type.POST),
+        eq(resourceDef))).andReturn(request);
+
+    expect(requestHandler.handleRequest(request)).andReturn(result);
+    expect(request.getResultSerializer()).andReturn(resultSerializer);
+    expect(resultSerializer.serialize(result, uriInfo)).andReturn(serializedResult);
+    expect(result.isSynchronous()).andReturn(false).atLeastOnce();
+    expect(responseFactory.createResponse(Request.Type.POST, serializedResult, false)).andReturn(response);
+
+    replay(resourceDef, resultSerializer, requestFactory, responseFactory, request, requestHandler,
+        result, response, httpHeaders, uriInfo);
+
+    //test
+    HostService hostService = new TestHostService(resourceDef, clusterName, null, requestFactory, responseFactory, requestHandler);
+    assertSame(response, hostService.createHosts(body, httpHeaders, uriInfo));
 
 
     verify(resourceDef, resultSerializer, requestFactory, responseFactory, request, requestHandler,
     verify(resourceDef, resultSerializer, requestFactory, responseFactory, request, requestHandler,
         result, response, httpHeaders, uriInfo);
         result, response, httpHeaders, uriInfo);
@@ -291,7 +345,7 @@ public class HostServiceTest {
     }
     }
 
 
     @Override
     @Override
-    ResourceDefinition createResourceDefinition(String hostName, String clusterName) {
+    ResourceDefinition createResourceDefinition(String hostName, String clusterName, UriInfo ui) {
       assertEquals(m_clusterId, clusterName);
       assertEquals(m_clusterId, clusterName);
       assertEquals(m_hostId, hostName);
       assertEquals(m_hostId, hostName);
       return m_resourceDef;
       return m_resourceDef;

+ 15 - 12
ambari-server/src/test/java/org/apache/ambari/server/api/services/UpdatePersistenceManagerTest.java

@@ -23,11 +23,14 @@ import org.apache.ambari.server.api.resources.ResourceDefinition;
 import org.apache.ambari.server.controller.internal.RequestStatusImpl;
 import org.apache.ambari.server.controller.internal.RequestStatusImpl;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
 import org.apache.ambari.server.api.query.Query;
 import org.apache.ambari.server.api.query.Query;
+import org.apache.ambari.server.controller.spi.Request;
 import org.apache.ambari.server.controller.spi.*;
 import org.apache.ambari.server.controller.spi.*;
 import org.junit.Test;
 import org.junit.Test;
 
 
 import java.util.HashMap;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Map;
+import java.util.Set;
 
 
 import static org.junit.Assert.*;
 import static org.junit.Assert.*;
 
 
@@ -42,18 +45,17 @@ public class UpdatePersistenceManagerTest {
     ResourceDefinition resource = createMock(ResourceDefinition.class);
     ResourceDefinition resource = createMock(ResourceDefinition.class);
     ClusterController controller = createMock(ClusterController.class);
     ClusterController controller = createMock(ClusterController.class);
     Schema schema = createMock(Schema.class);
     Schema schema = createMock(Schema.class);
-    org.apache.ambari.server.controller.spi.Request serverRequest =
-        createStrictMock(org.apache.ambari.server.controller.spi.Request.class);
+    Request serverRequest = createStrictMock(Request.class);
     Query query = createMock(Query.class);
     Query query = createMock(Query.class);
     Predicate predicate = createMock(Predicate.class);
     Predicate predicate = createMock(Predicate.class);
 
 
-
+    Set<Map<PropertyId, Object>> setProperties = new HashSet<Map<PropertyId, Object>>();
     Map<PropertyId, Object> mapProperties = new HashMap<PropertyId, Object>();
     Map<PropertyId, Object> mapProperties = new HashMap<PropertyId, Object>();
     mapProperties.put(PropertyHelper.getPropertyId("bar", "foo"), "value");
     mapProperties.put(PropertyHelper.getPropertyId("bar", "foo"), "value");
+    setProperties.add(mapProperties);
 
 
     //expectations
     //expectations
     expect(resource.getType()).andReturn(Resource.Type.Component);
     expect(resource.getType()).andReturn(Resource.Type.Component);
-    expect(resource.getProperties()).andReturn(mapProperties);
     expect(resource.getQuery()).andReturn(query);
     expect(resource.getQuery()).andReturn(query);
     expect(query.getInternalPredicate()).andReturn(predicate);
     expect(query.getInternalPredicate()).andReturn(predicate);
 
 
@@ -61,7 +63,7 @@ public class UpdatePersistenceManagerTest {
 
 
     replay(resource, controller, schema, serverRequest, query, predicate);
     replay(resource, controller, schema, serverRequest, query, predicate);
 
 
-    new TestUpdatePersistenceManager(controller, mapProperties, serverRequest).persist(resource);
+    new TestUpdatePersistenceManager(controller, setProperties, serverRequest).persist(resource, setProperties);
 
 
     verify(resource, controller, schema, serverRequest, query, predicate);
     verify(resource, controller, schema, serverRequest, query, predicate);
   }
   }
@@ -69,14 +71,14 @@ public class UpdatePersistenceManagerTest {
   private class TestUpdatePersistenceManager extends UpdatePersistenceManager {
   private class TestUpdatePersistenceManager extends UpdatePersistenceManager {
 
 
     private ClusterController m_controller;
     private ClusterController m_controller;
-    private org.apache.ambari.server.controller.spi.Request m_request;
-    private Map<PropertyId, Object> m_mapProperties;
+    private Request m_request;
+    private Set<Map<PropertyId, Object>> m_setProperties;
 
 
     private TestUpdatePersistenceManager(ClusterController controller,
     private TestUpdatePersistenceManager(ClusterController controller,
-                                         Map<PropertyId, Object> mapProperties,
-                                         org.apache.ambari.server.controller.spi.Request controllerRequest) {
+                                         Set<Map<PropertyId, Object>> setProperties,
+                                         Request controllerRequest) {
       m_controller = controller;
       m_controller = controller;
-      m_mapProperties = mapProperties;
+      m_setProperties = setProperties;
       m_request = controllerRequest;
       m_request = controllerRequest;
     }
     }
 
 
@@ -86,8 +88,9 @@ public class UpdatePersistenceManagerTest {
     }
     }
 
 
     @Override
     @Override
-    protected org.apache.ambari.server.controller.spi.Request createControllerRequest(Map<PropertyId, Object> properties) {
-      assertEquals(m_mapProperties, properties);
+    protected Request createControllerRequest(Set<Map<PropertyId, Object>> setProperties) {
+      assertEquals(1, setProperties.size());
+      assertEquals(m_setProperties, setProperties);
       return m_request;
       return m_request;
     }
     }
   }
   }

+ 66 - 8
ambari-server/src/test/java/org/apache/ambari/server/api/services/parsers/JsonPropertyParserTest.java

@@ -23,9 +23,11 @@ import org.apache.ambari.server.controller.spi.PropertyId;
 import org.junit.Test;
 import org.junit.Test;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map;
+import java.util.Set;
 
 
 /**
 /**
  * Unit tests for JsonPropertyParser.
  * Unit tests for JsonPropertyParser.
@@ -45,10 +47,28 @@ public class JsonPropertyParserTest {
       "\"OuterCategory\" : { \"propName\" : 100, \"nested1\" : { \"nested2\" : { \"innerPropName\" : \"innerPropValue\" } } } }";
       "\"OuterCategory\" : { \"propName\" : 100, \"nested1\" : { \"nested2\" : { \"innerPropName\" : \"innerPropValue\" } } } }";
 
 
 
 
+  String clustersJson = "[ {" +
+      "\"Clusters\" : {\n" +
+      "    \"cluster_name\" : \"unitTestCluster1\"" +
+      "} }," +
+      "{" +
+      "\"Clusters\" : {\n" +
+      "    \"cluster_name\" : \"unitTestCluster2\"," +
+      "    \"property1\" : \"prop1Value\"" +
+      "} }," +
+      "{" +
+      "\"Clusters\" : {\n" +
+      "    \"cluster_name\" : \"unitTestCluster3\"," +
+      "    \"Category\" : { \"property2\" : \"prop2Value\"}" +
+      "} } ]";
+
+
   @Test
   @Test
   public void testParse() throws Exception {
   public void testParse() throws Exception {
     RequestBodyParser parser = new JsonPropertyParser();
     RequestBodyParser parser = new JsonPropertyParser();
-    Map<PropertyId, String> mapProps = parser.parse(serviceJson);
+    Set<Map<PropertyId, Object>> setProps = parser.parse(serviceJson);
+
+    assertEquals(1, setProps.size());
 
 
     Map<PropertyId, Object> mapExpected = new HashMap<PropertyId, Object>();
     Map<PropertyId, Object> mapExpected = new HashMap<PropertyId, Object>();
     mapExpected.put(PropertyHelper.getPropertyId("service_name", "Services"), "HDFS");
     mapExpected.put(PropertyHelper.getPropertyId("service_name", "Services"), "HDFS");
@@ -60,23 +80,61 @@ public class JsonPropertyParserTest {
     mapExpected.put(PropertyHelper.getPropertyId("propName", "OuterCategory"), "100");
     mapExpected.put(PropertyHelper.getPropertyId("propName", "OuterCategory"), "100");
     mapExpected.put(PropertyHelper.getPropertyId("innerPropName", "OuterCategory.nested1.nested2"), "innerPropValue");
     mapExpected.put(PropertyHelper.getPropertyId("innerPropName", "OuterCategory.nested1.nested2"), "innerPropValue");
 
 
-    assertEquals(mapExpected, mapProps);
+    assertEquals(mapExpected, setProps.iterator().next());
   }
   }
 
 
   @Test
   @Test
   public void testParse_NullBody() {
   public void testParse_NullBody() {
     RequestBodyParser parser = new JsonPropertyParser();
     RequestBodyParser parser = new JsonPropertyParser();
-    Map<PropertyId, String> mapProps = parser.parse(null);
-    assertNotNull(mapProps);
-    assertEquals(0, mapProps.size());
+    Set<Map<PropertyId, Object>> setProps = parser.parse(null);
+    assertNotNull(setProps);
+    assertEquals(0, setProps.size());
   }
   }
 
 
   @Test
   @Test
   public void testParse_EmptyBody() {
   public void testParse_EmptyBody() {
     RequestBodyParser parser = new JsonPropertyParser();
     RequestBodyParser parser = new JsonPropertyParser();
-    Map<PropertyId, String> mapProps = parser.parse("");
-    assertNotNull(mapProps);
-    assertEquals(0, mapProps.size());
+    Set<Map<PropertyId, Object>> setProps = parser.parse("");
+    assertNotNull(setProps);
+    assertEquals(0, setProps.size());
+  }
+
+  @Test
+  public void testParse_Array() {
+    RequestBodyParser parser = new JsonPropertyParser();
+    Set<Map<PropertyId, Object>> setProps = parser.parse(clustersJson);
+    assertEquals(3, setProps.size());
+
+    boolean cluster1Matches = false;
+    boolean cluster2Matches = false;
+    boolean cluster3Matches = false;
+
+    Map<PropertyId, String> mapCluster1 = new HashMap<PropertyId, String>();
+    mapCluster1.put(PropertyHelper.getPropertyId("cluster_name", "Clusters"), "unitTestCluster1");
+
+    Map<PropertyId, String> mapCluster2 = new HashMap<PropertyId, String>();
+    mapCluster2.put(PropertyHelper.getPropertyId("cluster_name", "Clusters"), "unitTestCluster2");
+    mapCluster2.put(PropertyHelper.getPropertyId("property1", "Clusters"), "prop1Value");
+
+
+    Map<PropertyId, String> mapCluster3 = new HashMap<PropertyId, String>();
+    mapCluster3.put(PropertyHelper.getPropertyId("cluster_name", "Clusters"), "unitTestCluster3");
+    mapCluster3.put(PropertyHelper.getPropertyId("property2", "Clusters.Category"), "prop2Value");
+
+
+    for (Map<PropertyId, Object> mapProps : setProps) {
+      if (mapProps.equals(mapCluster1)) {
+        cluster1Matches = true;
+      } else if (mapProps.equals(mapCluster2)) {
+        cluster2Matches = true;
+      } else if (mapProps.equals(mapCluster3)) {
+        cluster3Matches = true;
+      }
+    }
+
+    assertTrue(cluster1Matches);
+    assertTrue(cluster2Matches);
+    assertTrue(cluster3Matches);
   }
   }
 }
 }