浏览代码

AMBARI-12278. Pre RU - install failed and attempts to re-install continue to fail (dlysnichenko)

Lisnichenko Dmitro 10 年之前
父节点
当前提交
6f2b5c2f9c

+ 12 - 0
ambari-common/src/main/python/resource_management/libraries/functions/hdp_select.py

@@ -25,6 +25,7 @@ from resource_management.core.resources.system import Execute
 from resource_management.libraries.functions.default import default
 from resource_management.libraries.functions.get_hdp_version import get_hdp_version
 from resource_management.libraries.script.script import Script
+from resource_management.core.shell import call
 
 # hdp-select set oozie-server 2.2.0.0-1234
 TEMPLATE = ('hdp-select', 'set')
@@ -212,3 +213,14 @@ def _get_upgrade_stack():
     return (stack_name, stack_version)
 
   return None
+
+
+def get_hdp_versions():
+  code, out = call("hdp-select versions")
+  if 0 == code:
+    versions = []
+    for line in out.splitlines():
+      versions.append(line.rstrip('\n'))
+    return versions
+  else:
+    return []

+ 90 - 0
ambari-common/src/main/python/resource_management/libraries/functions/repo_version_history.py

@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+"""
+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 os
+from resource_management.core.logger import Logger
+
+"""
+Repository version file is used while executing install_packages.py
+That allows us to get actual installed version even during reinstalling existing
+ repository version (when hdp-select output does not change before and after
+ installing packages).
+
+Format:
+2.3.0.0,2.3.0.0-5678
+2.2.0.0,2.2.0.0-1699
+
+First value in each string should be normalized (contain no build number)
+"""
+
+
+# Mapping file used to store repository versions without a build number, and the actual version it corresponded to.
+# E.g., HDP 2.2.0.0 => HDP 2.2.0.0-2041
+REPO_VERSION_HISTORY_FILE = "/var/lib/ambari-agent/data/repo_version_history.csv"
+
+
+def read_actual_version_from_history_file(repository_version):
+  """
+  :param repository_version: normalized repo version (without build number) as received from the server
+  Search the repository version history file for a line that contains repository_version,actual_version
+  Notice that the parts are delimited by a comma.
+  :return: Return the actual_version if found, otherwise, return None.
+  """
+  actual_version = None
+  if os.path.isfile(REPO_VERSION_HISTORY_FILE):
+    with open(REPO_VERSION_HISTORY_FILE, "r") as f:
+      for line in reversed(f.readlines()):
+        line_parts = line.split(",")
+        if line_parts and len(line_parts) == 2 and line_parts[0] == repository_version:
+          item = line_parts[1].strip()
+          if item != "":
+            actual_version = item
+            break
+  return actual_version
+
+
+def write_actual_version_to_history_file(repository_version, actual_version):
+  """
+  Save the tuple of repository_version,actual_version to the repo version history file if the repository_version
+  doesn't already exist
+  :param repository_version: normalized repo version (without build number) as received from the server
+  :param actual_version: Repo version with the build number, as determined using hdp-select
+  :returns Return True if appended the values to the file, otherwise, return False.
+  """
+  wrote_value = False
+  if repository_version is None or actual_version is None:
+    return
+
+  if repository_version == "" or actual_version == "":
+    return
+
+  value = repository_version + "," + actual_version
+  key_exists = False
+  try:
+    if read_actual_version_from_history_file(repository_version) is None:
+      with open(REPO_VERSION_HISTORY_FILE, "a") as f:
+        f.write(repository_version + "," + actual_version + "\n")
+        wrote_value = True
+    if wrote_value:
+      Logger.info("Appended value \"{0}\" to file {1} to track this as a new version.".format(value, REPO_VERSION_HISTORY_FILE))
+  except Exception, err:
+    Logger.error("Failed to write to file {0} the value: {1}. Error: {2}".format(REPO_VERSION_HISTORY_FILE, value, str(err)))
+
+  return wrote_value

+ 24 - 87
ambari-server/src/main/resources/custom_actions/scripts/install_packages.py

@@ -22,18 +22,19 @@ Ambari Agent
 import os
 import signal
 
-import sys
 import re
 import os.path
 
-import ambari_simplejson as json # simplejson is much faster comparing to Python 2.6 json module and has the same functions set.
+import ambari_simplejson as json  # simplejson is much faster comparing to Python 2.6 json module and has the same functions set.
 
 from resource_management import *
 from resource_management.libraries.functions.list_ambari_managed_repos import list_ambari_managed_repos
 from ambari_commons.os_check import OSCheck, OSConst
 from resource_management.libraries.functions.packages_analyzer import allInstalledPackages
 from resource_management.libraries.functions import conf_select
-from resource_management.core.shell import call
+from resource_management.libraries.functions.hdp_select import get_hdp_versions
+from resource_management.libraries.functions.repo_version_history \
+  import read_actual_version_from_history_file, write_actual_version_to_history_file, REPO_VERSION_HISTORY_FILE
 
 from resource_management.core.logger import Logger
 
@@ -50,10 +51,6 @@ class InstallPackages(Script):
   REPO_FILE_NAME_PREFIX = 'HDP-'
   STACK_TO_ROOT_FOLDER = {"HDP": "/usr/hdp"}
   
-  # Mapping file used to store repository versions without a build number, and the actual version it corresponded to.
-  # E.g., HDP 2.2.0.0 => HDP 2.2.0.0-2041
-  REPO_VERSION_HISTORY_FILE = "/var/lib/ambari-agent/data/repo_version_history.json"
-
   def actionexecute(self, env):
     num_errors = 0
 
@@ -132,11 +129,14 @@ class InstallPackages(Script):
       m = re.search("[\d\.]+-\d+", self.repository_version)
       if m:
         # Contains a build number
-        self.structured_output['actual_version'] = self.repository_version  # This is the best value known so far.
+        self.repo_version_with_build_number = self.repository_version
+        self.structured_output['actual_version'] = self.repo_version_with_build_number  # This is the best value known so far.
         self.put_structured_out(self.structured_output)
+      else:
+        self.repo_version_with_build_number = None
 
     # Initial list of versions, used to compute the new version installed
-    self.old_versions = self.hdp_versions()
+    self.old_versions = get_hdp_versions()
 
     try:
       # It's possible for the process to receive a SIGTERM while installing the packages
@@ -158,60 +158,6 @@ class InstallPackages(Script):
       self.structured_output['package_installation_result'] == 'SUCCESS':
       conf_select.create_config_links(stack_id, self.structured_output['actual_version'])
 
-  def get_actual_version_from_file(self):
-    """
-    Search the repository version history file for a line that contains repository_version,actual_version
-    Notice that the parts are delimited by a comma.
-    :return: Return the actual_version if found, otherwise, return None.
-    """
-    actual_version = None
-    if os.path.isfile(self.REPO_VERSION_HISTORY_FILE):
-      with open(self.REPO_VERSION_HISTORY_FILE, "r") as f:
-        for line in f.readlines():
-          line_parts = line.split(",")
-          if line_parts and len(line_parts) == 2 and line_parts[0] == self.repository_version:
-            item = line_parts[1].strip()
-            if item != "":
-              actual_version = item
-              break
-    return actual_version
-
-  def write_actual_version_to_file(self, actual_version):
-    """
-    Save the tuple of repository_version,actual_version to the repo version history file if the repository_version
-    doesn't already exist
-    :param actual_version: Repo version with the build number
-    :returns Return True if appended the values to the file, otherwise, return False.
-    """
-    wrote_value = False
-    if self.repository_version is None or actual_version is None:
-      return
-
-    if self.repository_version == "" or actual_version == "":
-      return
-
-    value = self.repository_version + "," + actual_version
-    key_exists = False
-    try:
-      if os.path.isfile(self.REPO_VERSION_HISTORY_FILE):
-        with open(self.REPO_VERSION_HISTORY_FILE, "r") as f:
-          for line in f.readlines():
-            line_parts = line.split(",")
-            if line_parts and len(line_parts) == 2 and line_parts[0] == self.repository_version:
-              key_exists = True
-              break
-
-      if not key_exists:
-        with open(self.REPO_VERSION_HISTORY_FILE, "a") as f:
-          f.write(self.repository_version + "," + actual_version + "\n")
-          wrote_value = True
-      if wrote_value:
-        Logger.info("Appended value \"{0}\" to file {1} to track this as a new version.".format(value, self.REPO_VERSION_HISTORY_FILE))
-    except Exception, err:
-      Logger.error("Failed to write to file {0} the value: {1}. Error: {2}".format(self.REPO_VERSION_HISTORY_FILE, value, str(err)))
-
-    return wrote_value
-
   def compute_actual_version(self):
     """
     After packages are installed, determine what the new actual version is, in order to save it.
@@ -219,30 +165,34 @@ class InstallPackages(Script):
     Logger.info("Attempting to determine actual version with build number.")
     Logger.info("Old versions: {0}".format(self.old_versions))
 
-    new_versions = self.hdp_versions()
+    new_versions = get_hdp_versions()
     Logger.info("New versions: {0}".format(new_versions))
 
     deltas = set(new_versions) - set(self.old_versions)
     Logger.info("Deltas: {0}".format(deltas))
 
+    # Get HDP version without build number
+    normalized_repo_version = self.repository_version.split('-')[0]
+
     if 1 == len(deltas):
       self.actual_version = next(iter(deltas)).strip()
       self.structured_output['actual_version'] = self.actual_version
       self.put_structured_out(self.structured_output)
-      self.write_actual_version_to_file(self.actual_version)
+      write_actual_version_to_history_file(normalized_repo_version, self.actual_version)
     else:
       Logger.info("Cannot determine a new actual version installed by using the delta method.")
       # If the first install attempt does a partial install and is unable to report this to the server,
       # then a subsequent attempt will report an empty delta. For this reason, it is important to search the
       # repo version history file to determine if we previously did write an actual_version.
-      self.actual_version = self.get_actual_version_from_file()
+      self.actual_version = read_actual_version_from_history_file(normalized_repo_version)
       if self.actual_version is not None:
         self.actual_version = self.actual_version.strip()
         self.structured_output['actual_version'] = self.actual_version
         self.put_structured_out(self.structured_output)
-        Logger.info("Found actual version {0} by parsing file {1}".format(self.actual_version, self.REPO_VERSION_HISTORY_FILE))
-      else:
+        Logger.info("Found actual version {0} by parsing file {1}".format(self.actual_version, REPO_VERSION_HISTORY_FILE))
+      elif self.repo_version_with_build_number is None:
         # It's likely that this host does not have any Stack Components installed, so only contains AMS.
+        # So just use repo version value provided by server (we already put it to structured output)
         if not os.path.exists(self.stack_root_folder):
           # Special case when this host does not contain any HDP components, but still contains other components like AMS.
           msg = "Could not determine actual version. This stack's root directory ({0}) is not present on this host, so this host does not contain any versionable components. " \
@@ -293,15 +243,12 @@ class InstallPackages(Script):
         for package in new_packages_installed:
           if package_version_string and (package_version_string in package):
             Package(package, action="remove")
-    else:
-      # Compute the actual version in order to save it in structured out
-      try:
-        self.compute_actual_version()
-      except Exception, err:
-        ret_code = 1
-        Logger.logger.exception("Failure while computing actual version. Error: {0}".format(str(err)))
-
-    pass
+    # Compute the actual version in order to save it in structured out
+    try:
+      self.compute_actual_version()
+    except Fail, err:
+      ret_code = 1
+      Logger.logger.exception("Failure while computing actual version. Error: {0}".format(str(err)))
     return ret_code
 
   def install_repository(self, url_info, append_to_file, template):
@@ -348,16 +295,6 @@ class InstallPackages(Script):
     else:
       return package_name
 
-  def hdp_versions(self):
-    code, out = call("hdp-select versions")
-    if 0 == code:
-      versions = []
-      for line in out.splitlines():
-        versions.append(line.rstrip('\n'))
-      return versions
-    else:
-      return []
-
   def abort_handler(self, signum, frame):
     Logger.error("Caught signal {0}, will handle it gracefully. Compute the actual version if possible before exiting.".format(signum))
     self.compute_actual_version()

+ 591 - 19
ambari-server/src/test/python/custom_actions/TestInstallPackages.py

@@ -20,19 +20,20 @@ limitations under the License.
 import json
 import os
 import pty
-import socket
 import subprocess
 import select
-from resource_management import Script,ConfigDictionary
+import install_packages
+
 from mock.mock import patch
 from mock.mock import MagicMock
 from stacks.utils.RMFTestCase import *
-from install_packages import InstallPackages
 from mock.mock import patch, MagicMock
 from resource_management.core.base import Resource
-from resource_management.core.resources.packaging import Package
 from resource_management.core.exceptions import Fail
-from ambari_commons.os_check import OSCheck
+
+OLD_VERSION_STUB = '2.1.0.0-400'
+VERSION_STUB_WITHOUT_BUILD_NUMBER = '2.2.0.1'
+VERSION_STUB = '2.2.0.1-885'
 
 subproc_mock = MagicMock()
 subproc_mock.return_value = MagicMock()
@@ -57,7 +58,15 @@ class TestInstallPackages(RMFTestCase):
   @patch("resource_management.libraries.functions.list_ambari_managed_repos.list_ambari_managed_repos")
   @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
   @patch("resource_management.libraries.script.Script.put_structured_out")
-  def test_normal_flow_rhel(self, put_structured_out_mock, allInstalledPackages_mock, list_ambari_managed_repos_mock):
+  @patch("resource_management.libraries.functions.hdp_select.get_hdp_versions")
+  @patch("resource_management.libraries.functions.repo_version_history.read_actual_version_from_history_file")
+  @patch("resource_management.libraries.functions.repo_version_history.write_actual_version_to_history_file")
+  def test_normal_flow_rhel(self,
+                            write_actual_version_to_history_file_mock,
+                            read_actual_version_from_history_file_mock,
+                            hdp_versions_mock,
+                            put_structured_out_mock, allInstalledPackages_mock, list_ambari_managed_repos_mock):
+    read_actual_version_from_history_file_mock.return_value = VERSION_STUB
     allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
     list_ambari_managed_repos_mock.return_value=[]
     self.executeScript("scripts/install_packages.py",
@@ -70,9 +79,9 @@ class TestInstallPackages(RMFTestCase):
     self.assertTrue(put_structured_out_mock.called)
     self.assertEquals(put_structured_out_mock.call_args[0][0],
                       {'package_installation_result': 'SUCCESS',
-                       'installed_repository_version': u'2.2.0.1-885',
+                       'installed_repository_version': VERSION_STUB,
                        'stack_id': 'HDP-2.2',
-                       'actual_version': u'2.2.0.1-885',
+                       'actual_version': VERSION_STUB,
                        'ambari_repositories': []})
     self.assertResourceCalled('Repository', 'HDP-UTILS-2.2.0.1-885',
                               base_url=u'http://repo1/HDP/centos5/2.x/updates/2.2.0.0',
@@ -105,8 +114,14 @@ class TestInstallPackages(RMFTestCase):
   @patch("resource_management.libraries.functions.list_ambari_managed_repos.list_ambari_managed_repos")
   @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
   @patch("resource_management.libraries.script.Script.put_structured_out")
-  def test_normal_flow_sles(self, put_structured_out_mock, allInstalledPackages_mock, list_ambari_managed_repos_mock, is_suse_family_mock):
+  @patch("resource_management.libraries.functions.hdp_select.get_hdp_versions")
+  @patch("resource_management.libraries.functions.repo_version_history.read_actual_version_from_history_file")
+  @patch("resource_management.libraries.functions.repo_version_history.write_actual_version_to_history_file")
+  def test_normal_flow_sles(self, write_actual_version_to_history_file_mock,
+                            read_actual_version_from_history_file_mock,
+                            hdp_versions_mock, put_structured_out_mock, allInstalledPackages_mock, list_ambari_managed_repos_mock, is_suse_family_mock):
     is_suse_family_mock = True
+    read_actual_version_from_history_file_mock.return_value = VERSION_STUB
     allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
     list_ambari_managed_repos_mock.return_value=[]
     self.executeScript("scripts/install_packages.py",
@@ -119,9 +134,9 @@ class TestInstallPackages(RMFTestCase):
     self.assertTrue(put_structured_out_mock.called)
     self.assertEquals(put_structured_out_mock.call_args[0][0],
                       {'package_installation_result': 'SUCCESS',
-                       'installed_repository_version': u'2.2.0.1-885',
+                       'installed_repository_version': VERSION_STUB,
                        'stack_id': 'HDP-2.2',
-                       'actual_version': u'2.2.0.1-885',
+                       'actual_version': VERSION_STUB,
                        'ambari_repositories': []})
     self.assertResourceCalled('Repository', 'HDP-UTILS-2.2.0.1-885',
                               base_url=u'http://repo1/HDP/centos5/2.x/updates/2.2.0.0',
@@ -155,8 +170,15 @@ class TestInstallPackages(RMFTestCase):
   @patch("ambari_commons.os_check.OSCheck.is_redhat_family")
   @patch("resource_management.libraries.script.Script.put_structured_out")
   @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
-  def test_exclude_existing_repo(self, allInstalledPackages_mock, put_structured_out_mock,
+  @patch("resource_management.libraries.functions.hdp_select.get_hdp_versions")
+  @patch("resource_management.libraries.functions.repo_version_history.read_actual_version_from_history_file")
+  @patch("resource_management.libraries.functions.repo_version_history.write_actual_version_to_history_file")
+  def test_exclude_existing_repo(self,  write_actual_version_to_history_file_mock,
+                                 read_actual_version_from_history_file_mock,
+                                 hdp_versions_mock,
+                                 allInstalledPackages_mock, put_structured_out_mock,
                                  is_redhat_family_mock, list_ambari_managed_repos_mock):
+    read_actual_version_from_history_file_mock.return_value = VERSION_STUB
     allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
     list_ambari_managed_repos_mock.return_value=["HDP-UTILS-2.2.0.1-885"]
     is_redhat_family_mock.return_value = True
@@ -170,9 +192,9 @@ class TestInstallPackages(RMFTestCase):
     self.assertTrue(put_structured_out_mock.called)
     self.assertEquals(put_structured_out_mock.call_args[0][0],
                       {'package_installation_result': 'SUCCESS',
-                       'installed_repository_version': u'2.2.0.1-885',
+                       'installed_repository_version': VERSION_STUB,
                        'stack_id': 'HDP-2.2',
-                       'actual_version': u'2.2.0.1-885',
+                       'actual_version': VERSION_STUB,
                        'ambari_repositories': ["HDP-UTILS-2.2.0.1-885"]})
     self.assertResourceCalled('Repository', 'HDP-UTILS-2.2.0.1-885',
                               base_url=u'http://repo1/HDP/centos5/2.x/updates/2.2.0.0',
@@ -247,8 +269,8 @@ class TestInstallPackages(RMFTestCase):
     self.assertTrue(put_structured_out_mock.called)
     self.assertEquals(put_structured_out_mock.call_args[0][0],
                       {'stack_id': 'HDP-2.2',
-                      'actual_version': u'2.2.0.1-885',
-                      'installed_repository_version': u'2.2.0.1-885',
+                      'actual_version': VERSION_STUB,
+                      'installed_repository_version': VERSION_STUB,
                       'ambari_repositories': [],
                       'package_installation_result': 'FAIL'})
     self.assertResourceCalled('Repository', 'HDP-UTILS-2.2.0.1-885',
@@ -278,8 +300,15 @@ class TestInstallPackages(RMFTestCase):
   @patch("resource_management.core.resources.packaging.Package")
   @patch("resource_management.libraries.script.Script.put_structured_out")
   @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
-  def test_format_package_name(self,allInstalledPackages_mock, put_structured_out_mock,
+  @patch("resource_management.libraries.functions.hdp_select.get_hdp_versions")
+  @patch("resource_management.libraries.functions.repo_version_history.read_actual_version_from_history_file")
+  @patch("resource_management.libraries.functions.repo_version_history.write_actual_version_to_history_file")
+  def test_format_package_name(self,                                                                                    write_actual_version_to_history_file_mock,
+                               read_actual_version_from_history_file_mock,
+                               hdp_versions_mock,
+                               allInstalledPackages_mock, put_structured_out_mock,
                                package_mock, is_suse_family_mock):
+    read_actual_version_from_history_file_mock.return_value = VERSION_STUB
     allInstalledPackages_mock = MagicMock(side_effect = TestInstallPackages._add_packages)
     is_suse_family_mock.return_value = True
     self.executeScript("scripts/install_packages.py",
@@ -292,9 +321,9 @@ class TestInstallPackages(RMFTestCase):
     self.assertTrue(put_structured_out_mock.called)
     self.assertEquals(put_structured_out_mock.call_args[0][0],
                       {'package_installation_result': 'SUCCESS',
-                       'installed_repository_version': u'2.2.0.1-885',
+                       'installed_repository_version': VERSION_STUB,
                        'stack_id': 'HDP-2.2',
-                       'actual_version': u'2.2.0.1-885',
+                       'actual_version': VERSION_STUB,
                        'ambari_repositories': []})
     self.assertResourceCalled('Repository', 'HDP-UTILS-2.2.0.1-885',
                               base_url=u'http://repo1/HDP/centos5/2.x/updates/2.2.0.0',
@@ -322,3 +351,546 @@ class TestInstallPackages(RMFTestCase):
     self.assertResourceCalled('Package', 'hadoop_2_2_0_1_885*-libhdfs', use_repos=['HDP-UTILS-2.2.0.1-885', 'HDP-2.2.0.1-885'], skip_repos=[])
     self.assertResourceCalled('Package', 'ambari-log4j', use_repos=['HDP-UTILS-2.2.0.1-885', 'HDP-2.2.0.1-885'], skip_repos=[])
     self.assertNoMoreResources()
+
+
+  @patch("resource_management.libraries.functions.list_ambari_managed_repos.list_ambari_managed_repos")
+  @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
+  @patch("resource_management.libraries.script.Script.put_structured_out")
+  @patch("resource_management.libraries.functions.hdp_select.get_hdp_versions")
+  @patch("resource_management.libraries.functions.repo_version_history.read_actual_version_from_history_file")
+  @patch("resource_management.libraries.functions.repo_version_history.write_actual_version_to_history_file")
+  def test_version_reporting__build_number_defined__usr_hdp_present(self,
+                                                                                   write_actual_version_to_history_file_mock,
+                                                                                   read_actual_version_from_history_file_mock,
+                                                                                   hdp_versions_mock,
+                                                                                   put_structured_out_mock, allInstalledPackages_mock, list_ambari_managed_repos_mock):
+    hdp_versions_mock.side_effect = [
+      [OLD_VERSION_STUB],  # before installation attempt
+      [OLD_VERSION_STUB, VERSION_STUB]
+    ]
+
+    config_file = self.get_src_folder() + "/test/python/custom_actions/configs/install_packages_config.json"
+    with open(config_file, "r") as f:
+      command_json = json.load(f)
+
+    command_json['roleParams']['repository_version'] = VERSION_STUB
+
+    allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
+    list_ambari_managed_repos_mock.return_value = []
+    self.executeScript("scripts/install_packages.py",
+                       classname="InstallPackages",
+                       command="actionexecute",
+                       config_dict=command_json,
+                       target=RMFTestCase.TARGET_CUSTOM_ACTIONS,
+                       os_type=('Redhat', '6.4', 'Final'),
+                       )
+    self.assertTrue(put_structured_out_mock.called)
+    self.assertEquals(put_structured_out_mock.call_args[0][0],
+                      {'package_installation_result': 'SUCCESS',
+                       'installed_repository_version': VERSION_STUB,
+                       'stack_id': 'HDP-2.2',
+                       'actual_version': VERSION_STUB,
+                       'ambari_repositories': []})
+    self.assertTrue(write_actual_version_to_history_file_mock.called)
+    self.assertEquals(write_actual_version_to_history_file_mock.call_args[0], (VERSION_STUB_WITHOUT_BUILD_NUMBER, VERSION_STUB))
+
+    hdp_versions_mock.reset_mock()
+    write_actual_version_to_history_file_mock.reset_mock()
+    put_structured_out_mock.reset_mock()
+
+    # Test retrying install again
+    hdp_versions_mock.side_effect = [
+      [OLD_VERSION_STUB, VERSION_STUB],
+      [OLD_VERSION_STUB, VERSION_STUB]
+    ]
+    read_actual_version_from_history_file_mock.return_value = VERSION_STUB
+
+    config_file = self.get_src_folder() + "/test/python/custom_actions/configs/install_packages_config.json"
+    with open(config_file, "r") as f:
+      command_json = json.load(f)
+
+    command_json['roleParams']['repository_version'] = VERSION_STUB
+
+    allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
+    list_ambari_managed_repos_mock.return_value = []
+    self.executeScript("scripts/install_packages.py",
+                       classname="InstallPackages",
+                       command="actionexecute",
+                       config_dict=command_json,
+                       target=RMFTestCase.TARGET_CUSTOM_ACTIONS,
+                       os_type=('Redhat', '6.4', 'Final'),
+                       )
+    self.assertTrue(put_structured_out_mock.called)
+    self.assertEquals(put_structured_out_mock.call_args[0][0],
+                      {'package_installation_result': 'SUCCESS',
+                       'installed_repository_version': VERSION_STUB,
+                       'stack_id': 'HDP-2.2',
+                       'actual_version': VERSION_STUB,
+                       'ambari_repositories': []})
+
+    self.assertFalse(write_actual_version_to_history_file_mock.called)
+
+
+
+  @patch("resource_management.libraries.functions.list_ambari_managed_repos.list_ambari_managed_repos")
+  @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
+  @patch("resource_management.libraries.script.Script.put_structured_out")
+  @patch("resource_management.libraries.functions.hdp_select.get_hdp_versions")
+  @patch("resource_management.libraries.functions.repo_version_history.read_actual_version_from_history_file")
+  @patch("resource_management.libraries.functions.repo_version_history.write_actual_version_to_history_file")
+  @patch("os.path.exists")
+  def test_version_reporting__build_number_defined__usr_hdp_not_present(self,
+                                                                    exists_mock,
+                                                                    write_actual_version_to_history_file_mock,
+                                                                    read_actual_version_from_history_file_mock,
+                                                                    hdp_versions_mock,
+                                                                    put_structured_out_mock, allInstalledPackages_mock, list_ambari_managed_repos_mock):
+    exists_mock.return_value = False
+    hdp_versions_mock.side_effect = [
+      [],  # before installation attempt
+      []
+    ]
+    read_actual_version_from_history_file_mock.return_value = None
+
+    config_file = self.get_src_folder() + "/test/python/custom_actions/configs/install_packages_config.json"
+    with open(config_file, "r") as f:
+      command_json = json.load(f)
+
+    command_json['roleParams']['repository_version'] = VERSION_STUB
+
+    allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
+    list_ambari_managed_repos_mock.return_value = []
+    self.executeScript("scripts/install_packages.py",
+                       classname="InstallPackages",
+                       command="actionexecute",
+                       config_dict=command_json,
+                       target=RMFTestCase.TARGET_CUSTOM_ACTIONS,
+                       os_type=('Redhat', '6.4', 'Final'),
+                       )
+
+    self.assertTrue(put_structured_out_mock.called)
+    self.assertEquals(put_structured_out_mock.call_args[0][0],
+                      {'package_installation_result': 'SUCCESS',
+                       'stack_id': u'HDP-2.2',
+                       'installed_repository_version': VERSION_STUB,
+                       'actual_version': VERSION_STUB,
+                       'ambari_repositories': []})
+
+    self.assertFalse(write_actual_version_to_history_file_mock.called)
+
+    hdp_versions_mock.reset_mock()
+    write_actual_version_to_history_file_mock.reset_mock()
+    put_structured_out_mock.reset_mock()
+
+    # Test retrying install again
+
+    hdp_versions_mock.side_effect = [
+      [],  # before installation attempt
+      []
+    ]
+    read_actual_version_from_history_file_mock.return_value = None
+
+    config_file = self.get_src_folder() + "/test/python/custom_actions/configs/install_packages_config.json"
+    with open(config_file, "r") as f:
+      command_json = json.load(f)
+
+    command_json['roleParams']['repository_version'] = VERSION_STUB
+
+    allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
+    list_ambari_managed_repos_mock.return_value = []
+    self.executeScript("scripts/install_packages.py",
+                       classname="InstallPackages",
+                       command="actionexecute",
+                       config_dict=command_json,
+                       target=RMFTestCase.TARGET_CUSTOM_ACTIONS,
+                       os_type=('Redhat', '6.4', 'Final'),
+                       )
+
+    self.assertTrue(put_structured_out_mock.called)
+    self.assertEquals(put_structured_out_mock.call_args[0][0],
+                      {'package_installation_result': 'SUCCESS',
+                       'stack_id': u'HDP-2.2',
+                       'installed_repository_version': VERSION_STUB,
+                       'actual_version': VERSION_STUB,
+                       'ambari_repositories': []})
+
+    self.assertFalse(write_actual_version_to_history_file_mock.called)
+
+
+  @patch("resource_management.libraries.functions.list_ambari_managed_repos.list_ambari_managed_repos")
+  @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
+  @patch("resource_management.libraries.script.Script.put_structured_out")
+  @patch("resource_management.libraries.functions.hdp_select.get_hdp_versions")
+  @patch("resource_management.libraries.functions.repo_version_history.read_actual_version_from_history_file")
+  @patch("resource_management.libraries.functions.repo_version_history.write_actual_version_to_history_file")
+  @patch("os.path.exists")
+  def test_version_reporting__build_number_not_defined__usr_hdp_present__no_components_installed(self,
+                                                                            exists_mock,
+                                                                            write_actual_version_to_history_file_mock,
+                                                                            read_actual_version_from_history_file_mock,
+                                                                            hdp_versions_mock,
+                                                                            put_structured_out_mock, allInstalledPackages_mock, list_ambari_managed_repos_mock):
+    exists_mock.return_value = True
+    hdp_versions_mock.side_effect = [
+      [],  # before installation attempt
+      []
+    ]
+    read_actual_version_from_history_file_mock.return_value = None
+
+    config_file = self.get_src_folder() + "/test/python/custom_actions/configs/install_packages_config.json"
+    with open(config_file, "r") as f:
+      command_json = json.load(f)
+
+    command_json['roleParams']['repository_version'] = VERSION_STUB_WITHOUT_BUILD_NUMBER
+
+    allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
+    list_ambari_managed_repos_mock.return_value = []
+
+    try:
+      self.executeScript("scripts/install_packages.py",
+                         classname="InstallPackages",
+                         command="actionexecute",
+                         config_dict=command_json,
+                         target=RMFTestCase.TARGET_CUSTOM_ACTIONS,
+                         os_type=('Redhat', '6.4', 'Final'),
+                         )
+      self.fail("Should throw exception")
+    except Fail:
+      pass  # Expected
+
+    self.assertTrue(put_structured_out_mock.called)
+    self.assertEquals(put_structured_out_mock.call_args_list[-1][0][0],
+                      { 'ambari_repositories': [],
+                        'package_installation_result': 'FAIL',
+                        'installed_repository_version': '2.2.0.1',
+                        'stack_id': u'HDP-2.2'})
+
+    self.assertFalse(write_actual_version_to_history_file_mock.called)
+
+    hdp_versions_mock.reset_mock()
+    write_actual_version_to_history_file_mock.reset_mock()
+    put_structured_out_mock.reset_mock()
+
+
+  @patch("resource_management.libraries.functions.list_ambari_managed_repos.list_ambari_managed_repos")
+  @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
+  @patch("resource_management.libraries.script.Script.put_structured_out")
+  @patch("resource_management.libraries.functions.hdp_select.get_hdp_versions")
+  @patch("resource_management.libraries.functions.repo_version_history.read_actual_version_from_history_file")
+  @patch("resource_management.libraries.functions.repo_version_history.write_actual_version_to_history_file")
+  @patch("os.path.exists")
+  def test_version_reporting__build_number_not_defined__usr_hdp_not_present(self,
+                                                                        exists_mock,
+                                                                        write_actual_version_to_history_file_mock,
+                                                                        read_actual_version_from_history_file_mock,
+                                                                        hdp_versions_mock,
+                                                                        put_structured_out_mock, allInstalledPackages_mock, list_ambari_managed_repos_mock):
+    exists_mock.return_value = False
+    hdp_versions_mock.side_effect = [
+      [],  # before installation attempt
+      []
+    ]
+    read_actual_version_from_history_file_mock.return_value = None
+
+    config_file = self.get_src_folder() + "/test/python/custom_actions/configs/install_packages_config.json"
+    with open(config_file, "r") as f:
+      command_json = json.load(f)
+
+    command_json['roleParams']['repository_version'] = VERSION_STUB_WITHOUT_BUILD_NUMBER
+
+    allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
+    list_ambari_managed_repos_mock.return_value = []
+    self.executeScript("scripts/install_packages.py",
+                       classname="InstallPackages",
+                       command="actionexecute",
+                       config_dict=command_json,
+                       target=RMFTestCase.TARGET_CUSTOM_ACTIONS,
+                       os_type=('Redhat', '6.4', 'Final'),
+                       )
+
+    self.assertTrue(put_structured_out_mock.called)
+    self.assertEquals(put_structured_out_mock.call_args_list[-1][0][0],
+                      {'package_installation_result': 'SUCCESS',
+                       'stack_id': u'HDP-2.2',
+                       'installed_repository_version': '2.2.0.1',
+                       'ambari_repositories': []})
+
+    self.assertFalse(write_actual_version_to_history_file_mock.called)
+
+    hdp_versions_mock.reset_mock()
+    write_actual_version_to_history_file_mock.reset_mock()
+    put_structured_out_mock.reset_mock()
+
+    # Test retrying install again
+
+    hdp_versions_mock.side_effect = [
+      [],  # before installation attempt
+      []
+    ]
+    read_actual_version_from_history_file_mock.return_value = None
+
+    config_file = self.get_src_folder() + "/test/python/custom_actions/configs/install_packages_config.json"
+    with open(config_file, "r") as f:
+      command_json = json.load(f)
+
+    command_json['roleParams']['repository_version'] = VERSION_STUB
+
+    allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
+    list_ambari_managed_repos_mock.return_value = []
+    self.executeScript("scripts/install_packages.py",
+                       classname="InstallPackages",
+                       command="actionexecute",
+                       config_dict=command_json,
+                       target=RMFTestCase.TARGET_CUSTOM_ACTIONS,
+                       os_type=('Redhat', '6.4', 'Final'),
+                       )
+
+    self.assertTrue(put_structured_out_mock.called)
+    self.assertEquals(put_structured_out_mock.call_args[0][0],
+                      {'package_installation_result': 'SUCCESS',
+                       'stack_id': u'HDP-2.2',
+                       'installed_repository_version': VERSION_STUB,
+                       'actual_version': VERSION_STUB,
+                       'ambari_repositories': []})
+
+    self.assertFalse(write_actual_version_to_history_file_mock.called)
+
+
+  @patch("resource_management.libraries.functions.list_ambari_managed_repos.list_ambari_managed_repos")
+  @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
+  @patch("resource_management.libraries.script.Script.put_structured_out")
+  @patch("resource_management.libraries.functions.hdp_select.get_hdp_versions")
+  @patch("resource_management.libraries.functions.repo_version_history.read_actual_version_from_history_file")
+  @patch("resource_management.libraries.functions.repo_version_history.write_actual_version_to_history_file")
+  def test_version_reporting__build_number_not_defined__usr_hdp_present(self,
+                                                                    write_actual_version_to_history_file_mock,
+                                                                    read_actual_version_from_history_file_mock,
+                                                                    hdp_versions_mock,
+                                                                    put_structured_out_mock, allInstalledPackages_mock, list_ambari_managed_repos_mock):
+    hdp_versions_mock.side_effect = [
+      [OLD_VERSION_STUB],  # before installation attempt
+      [OLD_VERSION_STUB, VERSION_STUB]
+    ]
+
+    config_file = self.get_src_folder() + "/test/python/custom_actions/configs/install_packages_config.json"
+    with open(config_file, "r") as f:
+      command_json = json.load(f)
+
+    command_json['roleParams']['repository_version'] = VERSION_STUB_WITHOUT_BUILD_NUMBER
+
+    allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
+    list_ambari_managed_repos_mock.return_value = []
+    self.executeScript("scripts/install_packages.py",
+                       classname="InstallPackages",
+                       command="actionexecute",
+                       config_dict=command_json,
+                       target=RMFTestCase.TARGET_CUSTOM_ACTIONS,
+                       os_type=('Redhat', '6.4', 'Final'),
+                       )
+    self.assertTrue(put_structured_out_mock.called)
+    self.assertEquals(put_structured_out_mock.call_args[0][0],
+                      {'package_installation_result': 'SUCCESS',
+                       'installed_repository_version': VERSION_STUB_WITHOUT_BUILD_NUMBER,
+                       'stack_id': 'HDP-2.2',
+                       'actual_version': VERSION_STUB,
+                       'ambari_repositories': []})
+    self.assertTrue(write_actual_version_to_history_file_mock.called)
+    self.assertEquals(write_actual_version_to_history_file_mock.call_args[0], (VERSION_STUB_WITHOUT_BUILD_NUMBER, VERSION_STUB))
+
+    hdp_versions_mock.reset_mock()
+    write_actual_version_to_history_file_mock.reset_mock()
+    put_structured_out_mock.reset_mock()
+
+    # Test retrying install again
+    hdp_versions_mock.side_effect = [
+      [OLD_VERSION_STUB, VERSION_STUB],
+      [OLD_VERSION_STUB, VERSION_STUB]
+    ]
+    read_actual_version_from_history_file_mock.return_value = VERSION_STUB
+
+    config_file = self.get_src_folder() + "/test/python/custom_actions/configs/install_packages_config.json"
+    with open(config_file, "r") as f:
+      command_json = json.load(f)
+
+    command_json['roleParams']['repository_version'] = VERSION_STUB_WITHOUT_BUILD_NUMBER
+
+    allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
+    list_ambari_managed_repos_mock.return_value = []
+    self.executeScript("scripts/install_packages.py",
+                       classname="InstallPackages",
+                       command="actionexecute",
+                       config_dict=command_json,
+                       target=RMFTestCase.TARGET_CUSTOM_ACTIONS,
+                       os_type=('Redhat', '6.4', 'Final'),
+                       )
+    self.assertTrue(put_structured_out_mock.called)
+    self.assertEquals(put_structured_out_mock.call_args[0][0],
+                      {'package_installation_result': 'SUCCESS',
+                       'installed_repository_version': VERSION_STUB_WITHOUT_BUILD_NUMBER,
+                       'stack_id': 'HDP-2.2',
+                       'actual_version': VERSION_STUB,
+                       'ambari_repositories': []})
+
+    self.assertFalse(write_actual_version_to_history_file_mock.called)
+
+
+  @patch("resource_management.libraries.functions.list_ambari_managed_repos.list_ambari_managed_repos")
+  @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
+  @patch("resource_management.libraries.script.Script.put_structured_out")
+  @patch("resource_management.libraries.functions.hdp_select.get_hdp_versions")
+  @patch("resource_management.libraries.functions.repo_version_history.read_actual_version_from_history_file")
+  @patch("resource_management.libraries.functions.repo_version_history.write_actual_version_to_history_file")
+  def test_version_reporting__wrong_build_number_specified__usr_hdp_present(self,
+                                                                        write_actual_version_to_history_file_mock,
+                                                                        read_actual_version_from_history_file_mock,
+                                                                        hdp_versions_mock,
+                                                                        put_structured_out_mock, allInstalledPackages_mock, list_ambari_managed_repos_mock):
+    hdp_versions_mock.side_effect = [
+      [OLD_VERSION_STUB],  # before installation attempt
+      [OLD_VERSION_STUB, VERSION_STUB]
+    ]
+
+    config_file = self.get_src_folder() + "/test/python/custom_actions/configs/install_packages_config.json"
+    with open(config_file, "r") as f:
+      command_json = json.load(f)
+
+    command_json['roleParams']['repository_version'] = '2.2.0.1-500'  # User specified wrong build number
+
+    allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
+    list_ambari_managed_repos_mock.return_value = []
+    self.executeScript("scripts/install_packages.py",
+                       classname="InstallPackages",
+                       command="actionexecute",
+                       config_dict=command_json,
+                       target=RMFTestCase.TARGET_CUSTOM_ACTIONS,
+                       os_type=('Redhat', '6.4', 'Final'),
+                       )
+    self.assertTrue(put_structured_out_mock.called)
+    self.assertEquals(put_structured_out_mock.call_args[0][0],
+                      {'package_installation_result': 'SUCCESS',
+                       'installed_repository_version': '2.2.0.1-500',
+                       'stack_id': 'HDP-2.2',
+                       'actual_version': VERSION_STUB,
+                       'ambari_repositories': []})
+    self.assertTrue(write_actual_version_to_history_file_mock.called)
+    self.assertEquals(write_actual_version_to_history_file_mock.call_args[0], ('2.2.0.1', VERSION_STUB))
+
+    hdp_versions_mock.reset_mock()
+    write_actual_version_to_history_file_mock.reset_mock()
+    put_structured_out_mock.reset_mock()
+
+    # Test retrying install again
+    hdp_versions_mock.side_effect = [
+      [OLD_VERSION_STUB, VERSION_STUB],
+      [OLD_VERSION_STUB, VERSION_STUB]
+    ]
+    read_actual_version_from_history_file_mock.return_value = VERSION_STUB
+
+    config_file = self.get_src_folder() + "/test/python/custom_actions/configs/install_packages_config.json"
+    with open(config_file, "r") as f:
+      command_json = json.load(f)
+
+    command_json['roleParams']['repository_version'] = '2.2.0.1-500'  # User specified wrong build number
+
+    allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
+    list_ambari_managed_repos_mock.return_value = []
+    self.executeScript("scripts/install_packages.py",
+                       classname="InstallPackages",
+                       command="actionexecute",
+                       config_dict=command_json,
+                       target=RMFTestCase.TARGET_CUSTOM_ACTIONS,
+                       os_type=('Redhat', '6.4', 'Final'),
+                       )
+    self.assertTrue(put_structured_out_mock.called)
+    self.assertEquals(put_structured_out_mock.call_args[0][0],
+                      {'package_installation_result': 'SUCCESS',
+                       'installed_repository_version': '2.2.0.1-500',
+                       'stack_id': 'HDP-2.2',
+                       'actual_version': VERSION_STUB,
+                       'ambari_repositories': []})
+
+    self.assertFalse(write_actual_version_to_history_file_mock.called)
+
+
+  @patch("resource_management.libraries.functions.list_ambari_managed_repos.list_ambari_managed_repos")
+  @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
+  @patch("resource_management.libraries.script.Script.put_structured_out")
+  @patch("resource_management.libraries.functions.hdp_select.get_hdp_versions")
+  @patch("resource_management.libraries.functions.repo_version_history.read_actual_version_from_history_file")
+  @patch("resource_management.libraries.functions.repo_version_history.write_actual_version_to_history_file")
+  @patch("os.path.exists")
+  def test_version_reporting__wrong_build_number_specified__usr_hdp_absent(self,
+                                                                            exists_mock,
+                                                                            write_actual_version_to_history_file_mock,
+                                                                            read_actual_version_from_history_file_mock,
+                                                                            hdp_versions_mock,
+                                                                            put_structured_out_mock, allInstalledPackages_mock, list_ambari_managed_repos_mock):
+    exists_mock.return_value = False
+    hdp_versions_mock.side_effect = [
+      [],  # before installation attempt
+      []
+    ]
+    read_actual_version_from_history_file_mock.return_value = None
+
+    config_file = self.get_src_folder() + "/test/python/custom_actions/configs/install_packages_config.json"
+    with open(config_file, "r") as f:
+      command_json = json.load(f)
+
+    command_json['roleParams']['repository_version'] = VERSION_STUB_WITHOUT_BUILD_NUMBER
+
+    allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
+    list_ambari_managed_repos_mock.return_value = []
+    self.executeScript("scripts/install_packages.py",
+                       classname="InstallPackages",
+                       command="actionexecute",
+                       config_dict=command_json,
+                       target=RMFTestCase.TARGET_CUSTOM_ACTIONS,
+                       os_type=('Redhat', '6.4', 'Final'),
+                       )
+
+    self.assertTrue(put_structured_out_mock.called)
+    self.assertEquals(put_structured_out_mock.call_args_list[-1][0][0],
+                      {'package_installation_result': 'SUCCESS',
+                       'stack_id': u'HDP-2.2',
+                       'installed_repository_version': '2.2.0.1',
+                       'ambari_repositories': []})
+
+    self.assertFalse(write_actual_version_to_history_file_mock.called)
+
+    hdp_versions_mock.reset_mock()
+    write_actual_version_to_history_file_mock.reset_mock()
+    put_structured_out_mock.reset_mock()
+
+    # Test retrying install again
+
+    hdp_versions_mock.side_effect = [
+      [],  # before installation attempt
+      []
+    ]
+    read_actual_version_from_history_file_mock.return_value = None
+
+    config_file = self.get_src_folder() + "/test/python/custom_actions/configs/install_packages_config.json"
+    with open(config_file, "r") as f:
+      command_json = json.load(f)
+
+    command_json['roleParams']['repository_version'] = VERSION_STUB
+
+    allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
+    list_ambari_managed_repos_mock.return_value = []
+    self.executeScript("scripts/install_packages.py",
+                       classname="InstallPackages",
+                       command="actionexecute",
+                       config_dict=command_json,
+                       target=RMFTestCase.TARGET_CUSTOM_ACTIONS,
+                       os_type=('Redhat', '6.4', 'Final'),
+                       )
+
+    self.assertTrue(put_structured_out_mock.called)
+    self.assertEquals(put_structured_out_mock.call_args[0][0],
+                      {'package_installation_result': 'SUCCESS',
+                       'stack_id': u'HDP-2.2',
+                       'installed_repository_version': VERSION_STUB,
+                       'actual_version': VERSION_STUB,
+                       'ambari_repositories': []})
+
+    self.assertFalse(write_actual_version_to_history_file_mock.called)