瀏覽代碼

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.default import default
 from resource_management.libraries.functions.get_hdp_version import get_hdp_version
 from resource_management.libraries.functions.get_hdp_version import get_hdp_version
 from resource_management.libraries.script.script import Script
 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
 # hdp-select set oozie-server 2.2.0.0-1234
 TEMPLATE = ('hdp-select', 'set')
 TEMPLATE = ('hdp-select', 'set')
@@ -212,3 +213,14 @@ def _get_upgrade_stack():
     return (stack_name, stack_version)
     return (stack_name, stack_version)
 
 
   return None
   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 os
 import signal
 import signal
 
 
-import sys
 import re
 import re
 import os.path
 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 import *
 from resource_management.libraries.functions.list_ambari_managed_repos import list_ambari_managed_repos
 from resource_management.libraries.functions.list_ambari_managed_repos import list_ambari_managed_repos
 from ambari_commons.os_check import OSCheck, OSConst
 from ambari_commons.os_check import OSCheck, OSConst
 from resource_management.libraries.functions.packages_analyzer import allInstalledPackages
 from resource_management.libraries.functions.packages_analyzer import allInstalledPackages
 from resource_management.libraries.functions import conf_select
 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
 from resource_management.core.logger import Logger
 
 
@@ -50,10 +51,6 @@ class InstallPackages(Script):
   REPO_FILE_NAME_PREFIX = 'HDP-'
   REPO_FILE_NAME_PREFIX = 'HDP-'
   STACK_TO_ROOT_FOLDER = {"HDP": "/usr/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):
   def actionexecute(self, env):
     num_errors = 0
     num_errors = 0
 
 
@@ -132,11 +129,14 @@ class InstallPackages(Script):
       m = re.search("[\d\.]+-\d+", self.repository_version)
       m = re.search("[\d\.]+-\d+", self.repository_version)
       if m:
       if m:
         # Contains a build number
         # 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)
         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
     # Initial list of versions, used to compute the new version installed
-    self.old_versions = self.hdp_versions()
+    self.old_versions = get_hdp_versions()
 
 
     try:
     try:
       # It's possible for the process to receive a SIGTERM while installing the packages
       # 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':
       self.structured_output['package_installation_result'] == 'SUCCESS':
       conf_select.create_config_links(stack_id, self.structured_output['actual_version'])
       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):
   def compute_actual_version(self):
     """
     """
     After packages are installed, determine what the new actual version is, in order to save it.
     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("Attempting to determine actual version with build number.")
     Logger.info("Old versions: {0}".format(self.old_versions))
     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))
     Logger.info("New versions: {0}".format(new_versions))
 
 
     deltas = set(new_versions) - set(self.old_versions)
     deltas = set(new_versions) - set(self.old_versions)
     Logger.info("Deltas: {0}".format(deltas))
     Logger.info("Deltas: {0}".format(deltas))
 
 
+    # Get HDP version without build number
+    normalized_repo_version = self.repository_version.split('-')[0]
+
     if 1 == len(deltas):
     if 1 == len(deltas):
       self.actual_version = next(iter(deltas)).strip()
       self.actual_version = next(iter(deltas)).strip()
       self.structured_output['actual_version'] = self.actual_version
       self.structured_output['actual_version'] = self.actual_version
       self.put_structured_out(self.structured_output)
       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:
     else:
       Logger.info("Cannot determine a new actual version installed by using the delta method.")
       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,
       # 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
       # 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.
       # 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:
       if self.actual_version is not None:
         self.actual_version = self.actual_version.strip()
         self.actual_version = self.actual_version.strip()
         self.structured_output['actual_version'] = self.actual_version
         self.structured_output['actual_version'] = self.actual_version
         self.put_structured_out(self.structured_output)
         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.
         # 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):
         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.
           # 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. " \
           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:
         for package in new_packages_installed:
           if package_version_string and (package_version_string in package):
           if package_version_string and (package_version_string in package):
             Package(package, action="remove")
             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
     return ret_code
 
 
   def install_repository(self, url_info, append_to_file, template):
   def install_repository(self, url_info, append_to_file, template):
@@ -348,16 +295,6 @@ class InstallPackages(Script):
     else:
     else:
       return package_name
       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):
   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))
     Logger.error("Caught signal {0}, will handle it gracefully. Compute the actual version if possible before exiting.".format(signum))
     self.compute_actual_version()
     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 json
 import os
 import os
 import pty
 import pty
-import socket
 import subprocess
 import subprocess
 import select
 import select
-from resource_management import Script,ConfigDictionary
+import install_packages
+
 from mock.mock import patch
 from mock.mock import patch
 from mock.mock import MagicMock
 from mock.mock import MagicMock
 from stacks.utils.RMFTestCase import *
 from stacks.utils.RMFTestCase import *
-from install_packages import InstallPackages
 from mock.mock import patch, MagicMock
 from mock.mock import patch, MagicMock
 from resource_management.core.base import Resource
 from resource_management.core.base import Resource
-from resource_management.core.resources.packaging import Package
 from resource_management.core.exceptions import Fail
 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 = MagicMock()
 subproc_mock.return_value = 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.list_ambari_managed_repos.list_ambari_managed_repos")
   @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
   @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
   @patch("resource_management.libraries.script.Script.put_structured_out")
   @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
     allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
     list_ambari_managed_repos_mock.return_value=[]
     list_ambari_managed_repos_mock.return_value=[]
     self.executeScript("scripts/install_packages.py",
     self.executeScript("scripts/install_packages.py",
@@ -70,9 +79,9 @@ class TestInstallPackages(RMFTestCase):
     self.assertTrue(put_structured_out_mock.called)
     self.assertTrue(put_structured_out_mock.called)
     self.assertEquals(put_structured_out_mock.call_args[0][0],
     self.assertEquals(put_structured_out_mock.call_args[0][0],
                       {'package_installation_result': 'SUCCESS',
                       {'package_installation_result': 'SUCCESS',
-                       'installed_repository_version': u'2.2.0.1-885',
+                       'installed_repository_version': VERSION_STUB,
                        'stack_id': 'HDP-2.2',
                        'stack_id': 'HDP-2.2',
-                       'actual_version': u'2.2.0.1-885',
+                       'actual_version': VERSION_STUB,
                        'ambari_repositories': []})
                        'ambari_repositories': []})
     self.assertResourceCalled('Repository', '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',
                               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.list_ambari_managed_repos.list_ambari_managed_repos")
   @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
   @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
   @patch("resource_management.libraries.script.Script.put_structured_out")
   @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
     is_suse_family_mock = True
+    read_actual_version_from_history_file_mock.return_value = VERSION_STUB
     allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
     allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
     list_ambari_managed_repos_mock.return_value=[]
     list_ambari_managed_repos_mock.return_value=[]
     self.executeScript("scripts/install_packages.py",
     self.executeScript("scripts/install_packages.py",
@@ -119,9 +134,9 @@ class TestInstallPackages(RMFTestCase):
     self.assertTrue(put_structured_out_mock.called)
     self.assertTrue(put_structured_out_mock.called)
     self.assertEquals(put_structured_out_mock.call_args[0][0],
     self.assertEquals(put_structured_out_mock.call_args[0][0],
                       {'package_installation_result': 'SUCCESS',
                       {'package_installation_result': 'SUCCESS',
-                       'installed_repository_version': u'2.2.0.1-885',
+                       'installed_repository_version': VERSION_STUB,
                        'stack_id': 'HDP-2.2',
                        'stack_id': 'HDP-2.2',
-                       'actual_version': u'2.2.0.1-885',
+                       'actual_version': VERSION_STUB,
                        'ambari_repositories': []})
                        'ambari_repositories': []})
     self.assertResourceCalled('Repository', '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',
                               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("ambari_commons.os_check.OSCheck.is_redhat_family")
   @patch("resource_management.libraries.script.Script.put_structured_out")
   @patch("resource_management.libraries.script.Script.put_structured_out")
   @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
   @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):
                                  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
     allInstalledPackages_mock.side_effect = TestInstallPackages._add_packages
     list_ambari_managed_repos_mock.return_value=["HDP-UTILS-2.2.0.1-885"]
     list_ambari_managed_repos_mock.return_value=["HDP-UTILS-2.2.0.1-885"]
     is_redhat_family_mock.return_value = True
     is_redhat_family_mock.return_value = True
@@ -170,9 +192,9 @@ class TestInstallPackages(RMFTestCase):
     self.assertTrue(put_structured_out_mock.called)
     self.assertTrue(put_structured_out_mock.called)
     self.assertEquals(put_structured_out_mock.call_args[0][0],
     self.assertEquals(put_structured_out_mock.call_args[0][0],
                       {'package_installation_result': 'SUCCESS',
                       {'package_installation_result': 'SUCCESS',
-                       'installed_repository_version': u'2.2.0.1-885',
+                       'installed_repository_version': VERSION_STUB,
                        'stack_id': 'HDP-2.2',
                        '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"]})
                        'ambari_repositories': ["HDP-UTILS-2.2.0.1-885"]})
     self.assertResourceCalled('Repository', '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',
                               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.assertTrue(put_structured_out_mock.called)
     self.assertEquals(put_structured_out_mock.call_args[0][0],
     self.assertEquals(put_structured_out_mock.call_args[0][0],
                       {'stack_id': 'HDP-2.2',
                       {'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': [],
                       'ambari_repositories': [],
                       'package_installation_result': 'FAIL'})
                       'package_installation_result': 'FAIL'})
     self.assertResourceCalled('Repository', 'HDP-UTILS-2.2.0.1-885',
     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.core.resources.packaging.Package")
   @patch("resource_management.libraries.script.Script.put_structured_out")
   @patch("resource_management.libraries.script.Script.put_structured_out")
   @patch("resource_management.libraries.functions.packages_analyzer.allInstalledPackages")
   @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):
                                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)
     allInstalledPackages_mock = MagicMock(side_effect = TestInstallPackages._add_packages)
     is_suse_family_mock.return_value = True
     is_suse_family_mock.return_value = True
     self.executeScript("scripts/install_packages.py",
     self.executeScript("scripts/install_packages.py",
@@ -292,9 +321,9 @@ class TestInstallPackages(RMFTestCase):
     self.assertTrue(put_structured_out_mock.called)
     self.assertTrue(put_structured_out_mock.called)
     self.assertEquals(put_structured_out_mock.call_args[0][0],
     self.assertEquals(put_structured_out_mock.call_args[0][0],
                       {'package_installation_result': 'SUCCESS',
                       {'package_installation_result': 'SUCCESS',
-                       'installed_repository_version': u'2.2.0.1-885',
+                       'installed_repository_version': VERSION_STUB,
                        'stack_id': 'HDP-2.2',
                        'stack_id': 'HDP-2.2',
-                       'actual_version': u'2.2.0.1-885',
+                       'actual_version': VERSION_STUB,
                        'ambari_repositories': []})
                        'ambari_repositories': []})
     self.assertResourceCalled('Repository', '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',
                               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', '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.assertResourceCalled('Package', 'ambari-log4j', use_repos=['HDP-UTILS-2.2.0.1-885', 'HDP-2.2.0.1-885'], skip_repos=[])
     self.assertNoMoreResources()
     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)