You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jl...@apache.org on 2016/04/12 19:19:31 UTC

[2/2] ambari git commit: AMBARI-15662: Ambari server upgrade should reapply installed mpacks (jluniya)

AMBARI-15662: Ambari server upgrade should reapply installed mpacks (jluniya)


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

Branch: refs/heads/trunk
Commit: 5dddc529f359d8557535efa4bf9eb33add9cda60
Parents: e371ac5
Author: Jayush Luniya <jl...@hortonworks.com>
Authored: Tue Apr 12 10:19:24 2016 -0700
Committer: Jayush Luniya <jl...@hortonworks.com>
Committed: Tue Apr 12 10:19:24 2016 -0700

----------------------------------------------------------------------
 ambari-server/pom.xml                           |   1 +
 .../python/ambari_server/serverConfiguration.py |   6 +-
 .../main/python/ambari_server/serverUpgrade.py  |   2 +
 .../main/python/ambari_server/setupMpacks.py    | 127 +++++-
 .../src/test/python/TestAmbariServer.py         |   4 +-
 ambari-server/src/test/python/TestMpacks.py     | 448 +++++++++++++++++++
 .../MYSERVICE/1.0.0/metainfo.xml                |  18 +
 .../MYSERVICE/1.0.0/metainfo.xml                |  18 +
 .../MYSERVICE/2.0.0/metainfo.xml                |  18 +
 .../myservice-ambari-mpack-1.0.0.0/mpack.json   |  50 +++
 .../common-services/SERVICEA/1.0/metainfo.xml   |  18 +
 .../common-services/SERVICEA/2.0/metainfo.xml   |  18 +
 .../common-services/SERVICEB/1.0.0/metainfo.xml |  18 +
 .../common-services/SERVICEB/2.0.0/metainfo.xml |  18 +
 .../mystack-ambari-mpack-1.0.0.0/mpack.json     |  21 +
 .../stacks/MYSTACK/1.0/metainfo.xml             |  18 +
 .../MYSTACK/1.0/services/SERVICEA/metainfo.xml  |  18 +
 .../stacks/MYSTACK/1.1/metainfo.xml             |  18 +
 .../MYSTACK/1.1/services/SERVICEA/metainfo.xml  |  18 +
 .../stacks/MYSTACK/2.0/metainfo.xml             |  18 +
 .../MYSTACK/2.0/services/SERVICEA/metainfo.xml  |  18 +
 .../MYSTACK/2.0/services/SERVICEB/metainfo.xml  |  18 +
 .../common-services/SERVICEA/1.0/metainfo.xml   |  18 +
 .../common-services/SERVICEA/2.0/metainfo.xml   |  18 +
 .../common-services/SERVICEB/1.0.0/metainfo.xml |  18 +
 .../common-services/SERVICEB/2.0.0/metainfo.xml |  18 +
 .../common-services/SERVICEC/1.0.0/metainfo.xml |  18 +
 .../common-services/SERVICEC/2.0.0/metainfo.xml |  18 +
 .../mystack-ambari-mpack-1.0.0.1/mpack.json     |  21 +
 .../stacks/MYSTACK/1.0/metainfo.xml             |  18 +
 .../MYSTACK/1.0/services/SERVICEA/metainfo.xml  |  18 +
 .../stacks/MYSTACK/1.1/metainfo.xml             |  18 +
 .../MYSTACK/1.1/services/SERVICEA/metainfo.xml  |  18 +
 .../stacks/MYSTACK/2.0/metainfo.xml             |  18 +
 .../MYSTACK/2.0/services/SERVICEA/metainfo.xml  |  18 +
 .../MYSTACK/2.0/services/SERVICEB/metainfo.xml  |  18 +
 .../stacks/MYSTACK/3.0/metainfo.xml             |  18 +
 .../MYSTACK/3.0/services/SERVICEA/metainfo.xml  |  18 +
 .../MYSTACK/3.0/services/SERVICEB/metainfo.xml  |  18 +
 .../MYSTACK/3.0/services/SERVICEC/metainfo.xml  |  18 +
 .../src/test/resources/mpacks_replay.log        |   2 +
 41 files changed, 1215 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/pom.xml
----------------------------------------------------------------------
diff --git a/ambari-server/pom.xml b/ambari-server/pom.xml
index b36c4e1..0240733 100644
--- a/ambari-server/pom.xml
+++ b/ambari-server/pom.xml
@@ -254,6 +254,7 @@
             <exclude>src/test/resources/gsInstaller-hosts.txt</exclude>
             <exclude>src/test/resources/temporal_ganglia_data.txt</exclude>
             <exclude>src/test/resources/users.ldif</exclude>
+            <exclude>src/test/resources/mpacks_replay.log</exclude>
             <exclude>src/main/resources/hive-schema-0.10.0.oracle.sql</exclude>
             <exclude>src/main/resources/hive-schema-0.12.0.oracle.sql</exclude>
             <exclude>src/main/resources/db/serial</exclude>

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/main/python/ambari_server/serverConfiguration.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/python/ambari_server/serverConfiguration.py b/ambari-server/src/main/python/ambari_server/serverConfiguration.py
index 446b083..d5a388f 100644
--- a/ambari-server/src/main/python/ambari_server/serverConfiguration.py
+++ b/ambari-server/src/main/python/ambari_server/serverConfiguration.py
@@ -1354,7 +1354,7 @@ def get_resources_location(properties):
 #
 def get_stack_location(properties):
   stack_location = properties[STACK_LOCATION_KEY]
-  if stack_location is None:
+  if not stack_location:
     stack_location = configDefaults.STACK_LOCATION_DEFAULT
   return stack_location
 
@@ -1363,7 +1363,7 @@ def get_stack_location(properties):
 #
 def get_common_services_location(properties):
   common_services_location = properties[COMMON_SERVICES_PATH_PROPERTY]
-  if common_services_location is None:
+  if not common_services_location:
     common_services_location = configDefaults.COMMON_SERVICES_LOCATION_DEFAULT
   return common_services_location
 
@@ -1372,7 +1372,7 @@ def get_common_services_location(properties):
 #
 def get_mpacks_staging_location(properties):
   mpacks_staging_location = properties[MPACKS_STAGING_PATH_PROPERTY]
-  if mpacks_staging_location is None:
+  if not mpacks_staging_location:
     mpacks_staging_location = configDefaults.MPACKS_STAGING_LOCATION_DEFAULT
   return mpacks_staging_location
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/main/python/ambari_server/serverUpgrade.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/python/ambari_server/serverUpgrade.py b/ambari-server/src/main/python/ambari_server/serverUpgrade.py
index 9521d61..72c4185 100644
--- a/ambari-server/src/main/python/ambari_server/serverUpgrade.py
+++ b/ambari-server/src/main/python/ambari_server/serverUpgrade.py
@@ -47,6 +47,7 @@ from ambari_server.utils import compare_versions
 from ambari_server.serverUtils import is_server_runing, get_ambari_server_api_base
 from ambari_server.userInput import get_validated_string_input, get_prompt_default, read_password, get_YN_input
 from ambari_server.serverClassPath import ServerClassPath
+from ambari_server.setupMpacks import replay_mpack_logs
 
 # constants
 STACK_NAME_VER_SEP = "-"
@@ -336,6 +337,7 @@ def upgrade(args):
     raise FatalException(retcode, err)
 
   restore_custom_services()
+  replay_mpack_logs()
   try:
     update_database_name_property(upgrade=True)
   except FatalException:

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/main/python/ambari_server/setupMpacks.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/python/ambari_server/setupMpacks.py b/ambari-server/src/main/python/ambari_server/setupMpacks.py
index 45bb675..3aa071f 100644
--- a/ambari-server/src/main/python/ambari_server/setupMpacks.py
+++ b/ambari-server/src/main/python/ambari_server/setupMpacks.py
@@ -22,6 +22,7 @@ import os
 import shutil
 import tempfile
 import json
+import ast
 
 from ambari_commons.exceptions import FatalException
 from ambari_commons.inet_utils import download_file
@@ -33,6 +34,10 @@ from ambari_server.serverConfiguration import get_ambari_properties, get_ambari_
 from resource_management.core import sudo
 from resource_management.libraries.functions.tar_archive import extract_archive, get_archive_root_dir
 from resource_management.libraries.functions.version import compare_versions
+from ambari_server.setupActions import INSTALL_MPACK_ACTION, UPGRADE_MPACK_ACTION
+
+MPACKS_REPLAY_LOG_FILENAME = "mpacks_replay.log"
+MPACKS_CACHE_DIRNAME = "cache"
 
 class _named_dict(dict):
   """
@@ -175,9 +180,10 @@ def remove_symlinks(stack_location, service_definitions_location, staged_mpack_d
           print_info_msg("Removing symlink {0}".format(dir))
           sudo.unlink(dir)
 
-def purge_stacks_and_mpacks():
+def purge_stacks_and_mpacks(replay_mode=False):
   """
   Purge all stacks and management packs
+  :param replay_mode: Flag to indicate if purging in replay mode
   """
   # Get ambari mpacks config properties
   stack_location, service_definitions_location, mpacks_staging_location = get_mpack_properties()
@@ -192,7 +198,8 @@ def purge_stacks_and_mpacks():
     print_info_msg("Purging service definitions location: " + service_definitions_location)
     sudo.rmtree(service_definitions_location)
 
-  if os.path.exists(mpacks_staging_location):
+  # Don't purge mpacks staging directory in replay mode
+  if os.path.exists(mpacks_staging_location) and not replay_mode:
     print_info_msg("Purging mpacks staging location: " + mpacks_staging_location)
     sudo.rmtree(mpacks_staging_location)
     sudo.makedir(mpacks_staging_location, 0755)
@@ -206,7 +213,7 @@ def process_stack_definitions_artifact(artifact, artifact_source_dir, options):
   """
   # Get ambari mpack properties
   stack_location, service_definitions_location, mpacks_staging_location = get_mpack_properties()
-  for file in os.listdir(artifact_source_dir):
+  for file in sorted(os.listdir(artifact_source_dir)):
     if os.path.isfile(os.path.join(artifact_source_dir, file)):
       # Example: /var/lib/ambari-server/resources/stacks/stack_advisor.py
       create_symlink(artifact_source_dir, stack_location, file, options.force)
@@ -215,7 +222,7 @@ def process_stack_definitions_artifact(artifact, artifact_source_dir, options):
       dest_stack_dir = os.path.join(stack_location, file)
       if not os.path.exists(dest_stack_dir):
         sudo.makedir(dest_stack_dir, 0755)
-      for file in os.listdir(src_stack_dir):
+      for file in sorted(os.listdir(src_stack_dir)):
         if os.path.isfile(os.path.join(src_stack_dir, file)):
           create_symlink(src_stack_dir, dest_stack_dir, file, options.force)
         else:
@@ -223,13 +230,13 @@ def process_stack_definitions_artifact(artifact, artifact_source_dir, options):
           dest_stack_version_dir = os.path.join(dest_stack_dir, file)
           if not os.path.exists(dest_stack_version_dir):
             sudo.makedir(dest_stack_version_dir, 0755)
-          for file in os.listdir(src_stack_version_dir):
+          for file in sorted(os.listdir(src_stack_version_dir)):
             if file == "services":
               src_stack_services_dir = os.path.join(src_stack_version_dir, file)
               dest_stack_services_dir = os.path.join(dest_stack_version_dir, file)
               if not os.path.exists(dest_stack_services_dir):
                 sudo.makedir(dest_stack_services_dir, 0755)
-              for file in os.listdir(src_stack_services_dir):
+              for file in sorted(os.listdir(src_stack_services_dir)):
                 create_symlink(src_stack_services_dir, dest_stack_services_dir, file, options.force)
             else:
               create_symlink(src_stack_version_dir, dest_stack_version_dir, file, options.force)
@@ -269,12 +276,12 @@ def process_service_definitions_artifact(artifact, artifact_source_dir, options)
   """
   # Get ambari mpack properties
   stack_location, service_definitions_location, mpacks_staging_location = get_mpack_properties()
-  for file in os.listdir(artifact_source_dir):
+  for file in sorted(os.listdir(artifact_source_dir)):
     src_service_definitions_dir = os.path.join(artifact_source_dir, file)
     dest_service_definitions_dir = os.path.join(service_definitions_location, file)
     if not os.path.exists(dest_service_definitions_dir):
       sudo.makedir(dest_service_definitions_dir, 0755)
-    for file in os.listdir(src_service_definitions_dir):
+    for file in sorted(os.listdir(src_service_definitions_dir)):
       create_symlink(src_service_definitions_dir, dest_service_definitions_dir, file, options.force)
 
 def process_service_definition_artifact(artifact, artifact_source_dir, options):
@@ -321,9 +328,9 @@ def process_stack_extension_definitions_artifact(artifact, artifact_source_dir,
   if not service_versions_map:
     print_error_msg("Must provide service versions map for stack-extension-definitions artifact!")
     raise FatalException(-1, 'Must provide service versions map for stack-extension-definition artifact!')
-  for service_name in os.listdir(artifact_source_dir):
+  for service_name in sorted(os.listdir(artifact_source_dir)):
     source_service_path = os.path.join(artifact_source_dir, service_name)
-    for service_version in os.listdir(source_service_path):
+    for service_version in sorted(os.listdir(source_service_path)):
       source_service_version_path = os.path.join(source_service_path, service_version)
       for service_version_entry in service_versions_map:
         if service_name == service_version_entry.service_name and service_version == service_version_entry.service_version:
@@ -389,14 +396,15 @@ def search_mpacks(mpack_name, max_mpack_version=None):
   stack_location, service_definitions_location, mpacks_staging_location = get_mpack_properties()
   results = []
   if os.path.exists(mpacks_staging_location) and os.path.isdir(mpacks_staging_location):
-    staged_mpack_dirs = os.listdir(mpacks_staging_location)
+    staged_mpack_dirs = sorted(os.listdir(mpacks_staging_location))
     for dir in staged_mpack_dirs:
+      if dir == MPACKS_CACHE_DIRNAME:
+        continue
       staged_mpack_dir = os.path.join(mpacks_staging_location, dir)
       if os.path.isdir(staged_mpack_dir):
         staged_mpack_metadata = read_mpack_metadata(staged_mpack_dir)
         if not staged_mpack_metadata:
-          print_error_msg("Skipping malformed management pack {0}-{1}. Metadata file missing!".format(
-                  staged_mpack_name, staged_mpack_version))
+          print_error_msg("Skipping malformed management pack in directory {0}.".format(staged_mpack_dir))
           continue
         staged_mpack_name = staged_mpack_metadata.name
         staged_mpack_version = staged_mpack_metadata.version
@@ -427,8 +435,10 @@ def uninstall_mpack(mpack_name, mpack_version):
   stack_location, service_definitions_location, mpacks_staging_location = get_mpack_properties()
   found = False
   if os.path.exists(mpacks_staging_location) and os.path.isdir(mpacks_staging_location):
-    staged_mpack_dirs = os.listdir(mpacks_staging_location)
+    staged_mpack_dirs = sorted(os.listdir(mpacks_staging_location))
     for dir in staged_mpack_dirs:
+      if dir == MPACKS_CACHE_DIRNAME:
+        continue
       staged_mpack_dir = os.path.join(mpacks_staging_location, dir)
       if os.path.isdir(staged_mpack_dir):
         staged_mpack_metadata = read_mpack_metadata(staged_mpack_dir)
@@ -493,10 +503,11 @@ def validate_mpack_prerequisites(mpack_metadata):
     raise FatalException(-1, "Prerequisites for management pack {0}-{1} failed!".format(
             mpack_metadata.name, mpack_metadata.version))
 
-def install_mpack(options):
+def _install_mpack(options, replay_mode=False):
   """
   Install management pack
   :param options: Command line options
+  :param replay_mode: Flag to indicate if executing command in replay mode
   """
 
   mpack_path = options.mpack_path
@@ -508,6 +519,9 @@ def install_mpack(options):
 
   # Download management pack to a temp location
   tmp_archive_path = download_mpack(mpack_path)
+  if not (tmp_archive_path and os.path.exists(tmp_archive_path)):
+    print_error_msg("Management pack could not be downloaded!")
+    raise FatalException(-1, 'Management pack could not be downloaded!')
 
   # Expand management pack in temp directory
   tmp_root_dir = expand_mpack(tmp_archive_path)
@@ -518,14 +532,17 @@ def install_mpack(options):
     raise FatalException(-1, 'Malformed management pack {0}. Metadata file missing!'.format(mpack_path))
 
   # Validate management pack prerequisites
-  validate_mpack_prerequisites(mpack_metadata)
+  # Skip validation in replay mode
+  if not replay_mode:
+    validate_mpack_prerequisites(mpack_metadata)
 
   # Purge previously installed stacks and management packs
   if options.purge:
-    purge_stacks_and_mpacks()
+    purge_stacks_and_mpacks(replay_mode)
 
   # Get ambari mpack properties
   stack_location, service_definitions_location, mpacks_staging_location = get_mpack_properties()
+  mpacks_cache_location = os.path.join(mpacks_staging_location, MPACKS_CACHE_DIRNAME)
   # Create directories
   if not os.path.exists(stack_location):
     sudo.makedir(stack_location, 0755)
@@ -533,12 +550,15 @@ def install_mpack(options):
     sudo.makedir(service_definitions_location, 0755)
   if not os.path.exists(mpacks_staging_location):
     sudo.makedir(mpacks_staging_location, 0755)
+  if not os.path.exists(mpacks_cache_location):
+    sudo.makedir(mpacks_cache_location, 0755)
 
   # Stage management pack (Stage at /var/lib/ambari-server/resources/mpacks/mpack_name-mpack_version)
   mpack_name = mpack_metadata.name
   mpack_version = mpack_metadata.version
   mpack_dirname = mpack_name + "-" + mpack_version
   mpack_staging_dir = os.path.join(mpacks_staging_location, mpack_dirname)
+  mpack_archive_path = os.path.join(mpacks_cache_location, os.path.basename(tmp_archive_path))
 
   print_info_msg("Stage management pack {0}-{1} to staging location {2}".format(
           mpack_name, mpack_version, mpack_staging_dir))
@@ -551,6 +571,7 @@ def install_mpack(options):
       print_error_msg(error_msg)
       raise FatalException(-1, error_msg)
   shutil.move(tmp_root_dir, mpack_staging_dir)
+  shutil.move(tmp_archive_path, mpack_archive_path)
 
   # Process setup steps for all artifacts (stack-definitions, service-definitions, stack-extension-definitions)
   # in the management pack
@@ -580,12 +601,49 @@ def install_mpack(options):
       print_info_msg("Unknown artifact {0} of type {1}".format(artifact_name, artifact_type))
 
   print_info_msg("Management pack {0}-{1} successfully installed!".format(mpack_name, mpack_version))
-  return mpack_name, mpack_version, mpack_staging_dir
+  return mpack_name, mpack_version, mpack_staging_dir, mpack_archive_path
+
+def get_replay_log_file():
+  """
+  Helper function to get mpack replay log file path
+  :return: mpack replay log file path
+  """
+  stack_location, service_definitions_location, mpacks_staging_location = get_mpack_properties()
+  replay_log_file = os.path.join(mpacks_staging_location, MPACKS_REPLAY_LOG_FILENAME)
+  return replay_log_file
+
+def add_replay_log(mpack_command, mpack_archive_path, purge, force, verbose):
+  """
+  Helper function to add mpack replay log entry
+  :param mpack_command: mpack command
+  :param mpack_archive_path: mpack archive path (/var/lib/ambari-server/resources/mpacks/mpack.tar.gz)
+  :param purge: purge command line option
+  :param force: force command line option
+  :param verbose: verbose command line option
+  """
+  replay_log_file = get_replay_log_file()
+  log = { 'mpack_command' : mpack_command, 'mpack_path' : mpack_archive_path, 'purge' : purge, 'force' : force, 'verbose' : verbose }
+  with open(replay_log_file, "a") as replay_log:
+    replay_log.write("{0}\n".format(log))
 
-def upgrade_mpack(options):
+def install_mpack(options, replay_mode=False):
+  """
+  Install management pack
+  :param options: Command line options
+  :param replay_mode: Flag to indicate if executing command in replay mode
+  """
+  # Force install when replaying logs
+  if replay_mode:
+    options.force = True
+  (mpack_name, mpack_version, mpack_staging_dir, mpack_archive_path) = _install_mpack(options, replay_mode)
+  if not replay_mode:
+    add_replay_log(INSTALL_MPACK_ACTION, mpack_archive_path, options.purge, options.force, options.verbose)
+
+def upgrade_mpack(options, replay_mode=False):
   """
   Upgrade management pack
   :param options: command line options
+  :param replay_mode: Flag to indicate if executing command in replay mode
   """
   mpack_path = options.mpack_path
   if options.purge:
@@ -600,12 +658,39 @@ def upgrade_mpack(options):
 
   # Force install new management pack version
   options.force = True
-  (mpack_name, mpack_version, mpack_staging_dir) = install_mpack(options)
+  (mpack_name, mpack_version, mpack_staging_dir, mpack_archive_path) = _install_mpack(options, replay_mode)
 
   # Uninstall old management packs
   uninstall_mpacks(mpack_name, mpack_version)
 
-
+  print_info_msg("Management pack {0}-{1} successfully upgraded!".format(mpack_name, mpack_version))
+  if not replay_mode:
+    add_replay_log(UPGRADE_MPACK_ACTION, mpack_archive_path, options.purge, options.force, options.verbose)
+
+def replay_mpack_logs():
+  """
+  Replay mpack logs during ambari-server upgrade
+  """
+  replay_log_file = get_replay_log_file()
+  if os.path.exists(replay_log_file):
+    with open(replay_log_file, "r") as f:
+      for replay_log in f:
+        replay_log = replay_log.strip()
+        print_info_msg("===========================================================================================")
+        print_info_msg("Executing Mpack Replay Log :")
+        print_info_msg(replay_log)
+        print_info_msg("===========================================================================================")
+        replay_options = _named_dict(ast.literal_eval(replay_log))
+        if replay_options.mpack_command == INSTALL_MPACK_ACTION:
+          install_mpack(replay_options, replay_mode=True)
+        elif replay_options.mpack_command == UPGRADE_MPACK_ACTION:
+          upgrade_mpack(replay_options, replay_mode=True)
+        else:
+          error_msg = "Invalid mpack command {0} in mpack replay log {1}!".format(replay_options.mpack_command, replay_log_file)
+          print_error_msg(error_msg)
+          raise FatalException(-1, error_msg)
+  else:
+    print_info_msg("No mpack replay logs found. Skipping replaying mpack commands")
 
 
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/TestAmbariServer.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/TestAmbariServer.py b/ambari-server/src/test/python/TestAmbariServer.py
index 390e0fe..e96a7bd 100644
--- a/ambari-server/src/test/python/TestAmbariServer.py
+++ b/ambari-server/src/test/python/TestAmbariServer.py
@@ -5151,7 +5151,8 @@ class TestAmbariServer(TestCase):
   @patch("ambari_server.serverUpgrade.move_user_custom_actions")
   @patch("ambari_server.serverUpgrade.update_krb_jaas_login_properties")
   @patch("ambari_server.serverUpgrade.update_ambari_env")
-  def test_upgrade_from_161(self, update_ambari_env_mock, update_krb_jaas_login_properties_mock, move_user_custom_actions_mock, upgrade_local_repo_mock, get_ambari_properties_mock,
+  @patch("ambari_server.setupMpacks.get_replay_log_file")
+  def test_upgrade_from_161(self, get_replay_log_file_mock, update_ambari_env_mock, update_krb_jaas_login_properties_mock, move_user_custom_actions_mock, upgrade_local_repo_mock, get_ambari_properties_mock,
                             get_ambari_properties_2_mock, get_ambari_properties_3_mock, get_ambari_version_mock, write_property_mock,
                             is_root_mock, update_ambari_properties_mock, find_properties_file_mock, run_os_command_mock,
                             run_schema_upgrade_mock, read_ambari_user_mock, print_warning_msg_mock,
@@ -5215,6 +5216,7 @@ class TestAmbariServer(TestCase):
     properties2.process_pair(JDBC_DATABASE_NAME_PROPERTY, "ambari")
     properties2.process_pair(JDBC_DATABASE_PROPERTY, "postgres")
     get_ambari_properties_3_mock.side_effect = get_ambari_properties_2_mock.side_effect = [properties, properties2, properties2]
+    get_replay_log_file_mock.return_value = "/invalid_path/mpacks_replay.log"
 
     run_schema_upgrade_mock.return_value = 0
     read_ambari_user_mock.return_value = "custom_user"

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/TestMpacks.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/TestMpacks.py b/ambari-server/src/test/python/TestMpacks.py
new file mode 100644
index 0000000..68e00ee
--- /dev/null
+++ b/ambari-server/src/test/python/TestMpacks.py
@@ -0,0 +1,448 @@
+'''
+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 mock.mock import patch, MagicMock, call
+from ambari_commons.exceptions import FatalException
+from ambari_server.setupMpacks import install_mpack, upgrade_mpack, replay_mpack_logs
+from unittest import TestCase
+from ambari_server.serverConfiguration import STACK_LOCATION_KEY, COMMON_SERVICES_PATH_PROPERTY, MPACKS_STAGING_PATH_PROPERTY
+
+with patch.object(os, "geteuid", new=MagicMock(return_value=0)):
+  from resource_management.core import sudo
+  reload(sudo)
+
+def get_configs():
+  test_directory = os.path.dirname(os.path.abspath(__file__))
+  mpacks_directory = os.path.join(test_directory, "mpacks")
+  configs = {
+    STACK_LOCATION_KEY : "/var/lib/ambari-server/resources/stacks",
+    COMMON_SERVICES_PATH_PROPERTY : "/var/lib/ambari-server/resources/common-services",
+    MPACKS_STAGING_PATH_PROPERTY : mpacks_directory
+  }
+  return configs
+
+configs = get_configs()
+
+class TestMpacks(TestCase):
+
+  def test_install_mpack_with_no_mpack_path(self):
+    options = self._create_empty_options_mock()
+    fail = False
+    try:
+      install_mpack(options)
+    except FatalException as e:
+      self.assertEquals("Management pack not specified!", e.reason)
+      fail = True
+    self.assertTrue(fail)
+
+  @patch("ambari_server.setupMpacks.download_mpack")
+  def test_install_mpack_with_invalid_mpack_path(self, download_mpack_mock):
+    options = self._create_empty_options_mock()
+    options.mpack_path = "/invalid_path/mpack.tar.gz"
+    download_mpack_mock.return_value = None
+
+    fail = False
+    try:
+      install_mpack(options)
+    except FatalException as e:
+      self.assertEquals("Management pack could not be downloaded!", e.reason)
+      fail = True
+    self.assertTrue(fail)
+
+  @patch("os.path.exists")
+  @patch("ambari_server.setupMpacks.extract_archive")
+  @patch("ambari_server.setupMpacks.get_archive_root_dir")
+  @patch("ambari_server.setupMpacks.download_mpack")
+  def test_install_mpack_with_malformed_mpack(self, download_mpack_mock, get_archive_root_dir_mock, extract_archive_mock, os_path_exists_mock):
+    options = self._create_empty_options_mock()
+    options.mpack_path = "/path/to/mpack.tar.gz"
+    download_mpack_mock.return_value = "/tmp/mpack.tar.gz"
+    os_path_exists_mock.return_value = True
+    get_archive_root_dir_mock.return_value = None
+
+    fail = False
+    try:
+      install_mpack(options)
+    except FatalException as e:
+      self.assertEquals("Malformed management pack. Root directory missing!", e.reason)
+      fail = True
+    self.assertTrue(fail)
+
+    get_archive_root_dir_mock.return_value = "mpack"
+    os_path_exists_mock.side_effect = [True, False, False]
+    extract_archive_mock.return_value = None
+    fail = False
+    try:
+      install_mpack(options)
+    except FatalException as e:
+      self.assertEquals("Malformed management pack. Failed to expand management pack!", e.reason)
+      fail = True
+    self.assertTrue(fail)
+
+    get_archive_root_dir_mock.return_value = "mpack"
+    os_path_exists_mock.side_effect = [True, False, True, False]
+    extract_archive_mock.return_value = None
+    fail = False
+    try:
+      install_mpack(options)
+    except FatalException as e:
+      self.assertEquals("Malformed management pack {0}. Metadata file missing!".format(options.mpack_path), e.reason)
+      fail = True
+    self.assertTrue(fail)
+
+  @patch("os.path.exists")
+  @patch("shutil.move")
+  @patch("os.mkdir")
+  @patch("ambari_server.setupMpacks.create_symlink")
+  @patch("ambari_server.setupMpacks.get_ambari_version")
+  @patch("ambari_server.setupMpacks.get_ambari_properties")
+  @patch("ambari_server.setupMpacks.add_replay_log")
+  @patch("ambari_server.setupMpacks.purge_stacks_and_mpacks")
+  @patch("ambari_server.setupMpacks.expand_mpack")
+  @patch("ambari_server.setupMpacks.download_mpack")
+  def test_install_stack_mpack(self, download_mpack_mock, expand_mpack_mock, purge_stacks_and_mpacks_mock,
+                                     add_replay_log_mock, get_ambari_properties_mock, get_ambari_version_mock,
+                                     create_symlink_mock, os_mkdir_mock, shutil_move_mock, os_path_exists_mock):
+    options = self._create_empty_options_mock()
+    options.mpack_path = "/path/to/mystack.tar.gz"
+    options.purge = True
+    download_mpack_mock.return_value = "/tmp/mystack.tar.gz"
+    expand_mpack_mock.return_value = "mpacks/mystack-ambari-mpack-1.0.0.0"
+    get_ambari_version_mock.return_value = "2.4.0.0"
+    """
+    os_path_exists_calls = [call('/tmp/mystack.tar.gz'),
+                            call('mpacks/mystack-ambari-mpack-1.0.0.0/mpack.json'),
+                            call('/var/lib/ambari-server/resources/stacks'),
+                            call('/var/lib/ambari-server/resources/common-services'),
+                            call(mpacks_directory),
+                            call(mpacks_directory + '/cache'),
+                            call(mpacks_directory + '/mystack-ambari-mpack-1.0.0.0'),
+                            call('/var/lib/ambari-server/resources/common-services/SERVICEA'),
+                            call('/var/lib/ambari-server/resources/common-services/SERVICEB'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/1.0'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/1.0/services'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/1.1'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/1.1/services'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/2.0'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/2.0/services')]
+   """
+    os_path_exists_mock.side_effect = [True, True, False, False, False, False,
+                                       False, False, False, False, False, False,
+                                       False, False, False, False]
+    get_ambari_properties_mock.return_value = configs
+    shutil_move_mock.return_value = True
+
+    install_mpack(options)
+
+    stacks_directory = configs[STACK_LOCATION_KEY]
+    common_services_directory = configs[COMMON_SERVICES_PATH_PROPERTY]
+    mpacks_directory = configs[MPACKS_STAGING_PATH_PROPERTY]
+    mpacks_staging_directory = os.path.join(mpacks_directory, "mystack-ambari-mpack-1.0.0.0")
+
+    os_mkdir_calls = [
+      call(stacks_directory),
+      call(common_services_directory),
+      call(mpacks_directory),
+      call(mpacks_directory + '/cache'),
+      call(os.path.join(common_services_directory, "SERVICEA")),
+      call(os.path.join(common_services_directory, "SERVICEB")),
+      call(os.path.join(stacks_directory, "MYSTACK")),
+      call(os.path.join(stacks_directory, "MYSTACK/1.0")),
+      call(os.path.join(stacks_directory, "MYSTACK/1.0/services")),
+      call(os.path.join(stacks_directory, "MYSTACK/1.1")),
+      call(os.path.join(stacks_directory, "MYSTACK/1.1/services")),
+      call(os.path.join(stacks_directory, "MYSTACK/2.0")),
+      call(os.path.join(stacks_directory, "MYSTACK/2.0/services"))
+    ]
+    create_symlink_calls = [
+      call(os.path.join(mpacks_staging_directory, "common-services/SERVICEA"),
+           os.path.join(common_services_directory, "SERVICEA"),
+           "1.0", None),
+      call(os.path.join(mpacks_staging_directory, "common-services/SERVICEA"),
+           os.path.join(common_services_directory, "SERVICEA"),
+           "2.0", None),
+      call(os.path.join(mpacks_staging_directory, "common-services/SERVICEB"),
+           os.path.join(common_services_directory, "SERVICEB"),
+           "1.0.0", None),
+      call(os.path.join(mpacks_staging_directory, "common-services/SERVICEB"),
+           os.path.join(common_services_directory, "SERVICEB"),
+           "2.0.0", None),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/1.0"),
+           os.path.join(stacks_directory, "MYSTACK/1.0"),
+           "metainfo.xml", None),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/1.0/services"),
+           os.path.join(stacks_directory, "MYSTACK/1.0/services"),
+           "SERVICEA", None),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/1.1"),
+           os.path.join(stacks_directory, "MYSTACK/1.1"),
+           "metainfo.xml", None),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/1.1/services"),
+           os.path.join(stacks_directory, "MYSTACK/1.1/services"),
+           "SERVICEA", None),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/2.0"),
+           os.path.join(stacks_directory, "MYSTACK/2.0"),
+           "metainfo.xml", None),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/2.0/services"),
+           os.path.join(stacks_directory, "MYSTACK/2.0/services"),
+           "SERVICEA", None),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/2.0/services"),
+           os.path.join(stacks_directory, "MYSTACK/2.0/services"),
+           "SERVICEB", None)
+    ]
+
+    self.assertTrue(purge_stacks_and_mpacks_mock.called)
+    os_mkdir_mock.assert_has_calls(os_mkdir_calls)
+    create_symlink_mock.assert_has_calls(create_symlink_calls)
+    self.assertTrue(add_replay_log_mock.called)
+
+  @patch("os.path.exists")
+  @patch("os.path.isdir")
+  @patch("os.symlink")
+  @patch("shutil.move")
+  @patch("os.mkdir")
+  @patch("ambari_server.setupMpacks.create_symlink")
+  @patch("ambari_server.setupMpacks.get_ambari_version")
+  @patch("ambari_server.setupMpacks.get_ambari_properties")
+  @patch("ambari_server.setupMpacks.add_replay_log")
+  @patch("ambari_server.setupMpacks.purge_stacks_and_mpacks")
+  @patch("ambari_server.setupMpacks.expand_mpack")
+  @patch("ambari_server.setupMpacks.download_mpack")
+  def test_install_addon_service_mpack(self, download_mpack_mock, expand_mpack_mock, purge_stacks_and_mpacks_mock,
+                                       add_replay_log_mock, get_ambari_properties_mock, get_ambari_version_mock,
+                                       create_symlink_mock, os_mkdir_mock, shutil_move_mock,os_symlink_mock,
+                                       os_path_isdir_mock, os_path_exists_mock):
+    options = self._create_empty_options_mock()
+    options.mpack_path = "/path/to/myservice.tar.gz"
+    options.purge = False
+    download_mpack_mock.return_value = "/tmp/myservice.tar.gz"
+    expand_mpack_mock.return_value = "mpacks/myservice-ambari-mpack-1.0.0.0"
+    get_ambari_version_mock.return_value = "2.4.0.0"
+    """
+    os_path_exists_calls = [call('/tmp/myservice.tar.gz'),
+                            call('mpacks/myservice-ambari-mpack-1.0.0.0/mpack.json'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/1.0'),
+                            call('/var/lib/ambari-server/resources/stacks'),
+                            call('/var/lib/ambari-server/resources/common-services'),
+                            call(mpacks_directory),
+                            call(mpacks_directory + '/cache'),
+                            call(mpacks_directory + '/myservice-ambari-mpack-1.0.0.0'),
+                            call('/var/lib/ambari-server/resources/common-services/MYSERVICE'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/1.0'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/1.0/services'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/2.0'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/2.0/services')]
+    """
+    os_path_exists_mock.side_effect = [True, True, True, True, True,
+                                       True, True, False, False, True,
+                                       True, True, True, True, True]
+
+    get_ambari_properties_mock.return_value = configs
+    shutil_move_mock.return_value = True
+    os_path_isdir_mock.return_value = True
+
+    install_mpack(options)
+
+    stacks_directory = configs[STACK_LOCATION_KEY]
+    common_services_directory = configs[COMMON_SERVICES_PATH_PROPERTY]
+    mpacks_directory = configs[MPACKS_STAGING_PATH_PROPERTY]
+    mpacks_staging_directory = os.path.join(mpacks_directory, "myservice-ambari-mpack-1.0.0.0")
+
+    os_mkdir_calls = [
+      call(os.path.join(common_services_directory, "MYSERVICE"))
+    ]
+    os_symlink_calls = [
+      call(os.path.join(mpacks_staging_directory, "common-services/MYSERVICE/1.0.0"),
+           os.path.join(common_services_directory, "MYSERVICE/1.0.0")),
+      call(os.path.join(mpacks_staging_directory, "custom-services/MYSERVICE/1.0.0"),
+           os.path.join(stacks_directory, "MYSTACK/1.0/services/MYSERVICE")),
+      call(os.path.join(mpacks_staging_directory, "custom-services/MYSERVICE/2.0.0"),
+           os.path.join(stacks_directory, "MYSTACK/2.0/services/MYSERVICE"))
+    ]
+
+    self.assertFalse(purge_stacks_and_mpacks_mock.called)
+    os_mkdir_mock.assert_has_calls(os_mkdir_calls)
+    os_symlink_mock.assert_has_calls(os_symlink_calls)
+    self.assertTrue(add_replay_log_mock.called)
+
+  @patch("os.path.exists")
+  @patch("shutil.move")
+  @patch("os.mkdir")
+  @patch("ambari_server.setupMpacks.create_symlink")
+  @patch("ambari_server.setupMpacks.get_ambari_version")
+  @patch("ambari_server.setupMpacks.get_ambari_properties")
+  @patch("ambari_server.setupMpacks.add_replay_log")
+  @patch("ambari_server.setupMpacks.uninstall_mpack")
+  @patch("ambari_server.setupMpacks.purge_stacks_and_mpacks")
+  @patch("ambari_server.setupMpacks.expand_mpack")
+  @patch("ambari_server.setupMpacks.download_mpack")
+  def test_upgrade_stack_mpack(self, download_mpack_mock, expand_mpack_mock, purge_stacks_and_mpacks_mock,
+                               uninstall_mpack_mock, add_replay_log_mock, get_ambari_properties_mock,
+                               get_ambari_version_mock, create_symlink_mock, os_mkdir_mock, shutil_move_mock,
+                               os_path_exists_mock):
+    options = self._create_empty_options_mock()
+    options.mpack_path = "/path/to/mystack-1.0.0.1.tar.gz"
+    download_mpack_mock.return_value = "/tmp/mystack-1.0.0.1.tar.gz"
+    expand_mpack_mock.return_value = "mpacks/mystack-ambari-mpack-1.0.0.1"
+    get_ambari_version_mock.return_value = "2.4.0.0"
+    """
+    os_path_exists_calls = [call('/tmp/mystack-1.0.0.1.tar.gz'),
+                            call('mpacks/mystack-ambari-mpack-1.0.0.1/mpack.json'),
+                            call('/var/lib/ambari-server/resources/stacks'),
+                            call('/var/lib/ambari-server/resources/common-services'),
+                            call(mpacks_directory),
+                            call(mpacks_directory + '/cache'),
+                            call(mpacks_directory + '/mystack-ambari-mpack-1.0.0.1'),
+                            call('/var/lib/ambari-server/resources/common-services/SERVICEA'),
+                            call('/var/lib/ambari-server/resources/common-services/SERVICEB'),
+                            call('/var/lib/ambari-server/resources/common-services/SERVICEC'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/1.0'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/1.0/services'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/1.1'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/1.1/services'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/2.0'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/2.0/services')]
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/3.0'),
+                            call('/var/lib/ambari-server/resources/stacks/MYSTACK/3.0/services'),
+                            call(mpacks_directory),
+                            call(mpacks_directory + '/myservice-ambari-mpack-1.0.0.0/mpack.json'),
+                            call(mpacks_directory + '/mystack-ambari-mpack-1.0.0.0/mpack.json'),
+                            call(mpacks_directory + '/mystack-ambari-mpack-1.0.0.1/mpack.json')]
+   """
+    os_path_exists_mock.side_effect = [True, True, True, True, True, True,
+                                       False, True, True, False, True, True, True,
+                                       True, True, True, True, False, False,
+                                       True, True, True, True]
+    get_ambari_properties_mock.return_value = configs
+    shutil_move_mock.return_value = True
+
+    upgrade_mpack(options)
+
+    stacks_directory = configs[STACK_LOCATION_KEY]
+    common_services_directory = configs[COMMON_SERVICES_PATH_PROPERTY]
+    mpacks_directory = configs[MPACKS_STAGING_PATH_PROPERTY]
+    mpacks_staging_directory = os.path.join(mpacks_directory, "mystack-ambari-mpack-1.0.0.1")
+
+
+    os_mkdir_calls = [
+      call(os.path.join(common_services_directory, "SERVICEC")),
+      call(os.path.join(stacks_directory, "MYSTACK/3.0")),
+      call(os.path.join(stacks_directory, "MYSTACK/3.0/services"))
+    ]
+    create_symlink_calls = [
+      call(os.path.join(mpacks_staging_directory, "common-services/SERVICEA"),
+           os.path.join(common_services_directory, "SERVICEA"),
+           "1.0", True),
+      call(os.path.join(mpacks_staging_directory, "common-services/SERVICEA"),
+           os.path.join(common_services_directory, "SERVICEA"),
+           "2.0", True),
+      call(os.path.join(mpacks_staging_directory, "common-services/SERVICEB"),
+           os.path.join(common_services_directory, "SERVICEB"),
+           "1.0.0", True),
+      call(os.path.join(mpacks_staging_directory, "common-services/SERVICEB"),
+           os.path.join(common_services_directory, "SERVICEB"),
+           "2.0.0", True),
+      call(os.path.join(mpacks_staging_directory, "common-services/SERVICEC"),
+           os.path.join(common_services_directory, "SERVICEC"),
+           "1.0.0", True),
+      call(os.path.join(mpacks_staging_directory, "common-services/SERVICEC"),
+           os.path.join(common_services_directory, "SERVICEC"),
+           "2.0.0", True),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/1.0"),
+           os.path.join(stacks_directory, "MYSTACK/1.0"),
+           "metainfo.xml", True),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/1.0/services"),
+           os.path.join(stacks_directory, "MYSTACK/1.0/services"),
+           "SERVICEA", True),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/1.1"),
+           os.path.join(stacks_directory, "MYSTACK/1.1"),
+           "metainfo.xml", True),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/1.1/services"),
+           os.path.join(stacks_directory, "MYSTACK/1.1/services"),
+           "SERVICEA", True),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/2.0"),
+           os.path.join(stacks_directory, "MYSTACK/2.0"),
+           "metainfo.xml", True),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/2.0/services"),
+           os.path.join(stacks_directory, "MYSTACK/2.0/services"),
+           "SERVICEA", True),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/2.0/services"),
+           os.path.join(stacks_directory, "MYSTACK/2.0/services"),
+           "SERVICEB", True),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/3.0"),
+           os.path.join(stacks_directory, "MYSTACK/3.0"),
+           "metainfo.xml", True),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/3.0/services"),
+           os.path.join(stacks_directory, "MYSTACK/3.0/services"),
+           "SERVICEA", True),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/3.0/services"),
+           os.path.join(stacks_directory, "MYSTACK/3.0/services"),
+           "SERVICEB", True),
+      call(os.path.join(mpacks_staging_directory, "stacks/MYSTACK/3.0/services"),
+           os.path.join(stacks_directory, "MYSTACK/3.0/services"),
+           "SERVICEC", True)
+    ]
+
+    self.assertFalse(purge_stacks_and_mpacks_mock.called)
+    os_mkdir_mock.assert_has_calls(os_mkdir_calls)
+    create_symlink_mock.assert_has_calls(create_symlink_calls)
+    uninstall_mpack_mock.assert_has_calls([call("mystack-ambari-mpack", "1.0.0.0")])
+    self.assertTrue(add_replay_log_mock.called)
+
+
+  @patch("os.path.exists")
+  @patch("ambari_server.setupMpacks.get_replay_log_file")
+  @patch("ambari_server.setupMpacks.upgrade_mpack")
+  @patch("ambari_server.setupMpacks.install_mpack")
+  def test_replay_mpack_logs(self, install_mpack_mock, upgrade_mpack_mock, get_replay_log_file_mock, os_path_exists_mock):
+    test_directory = os.path.dirname(os.path.abspath(__file__))
+    resources_directory = os.path.join(test_directory, os.pardir, "resources")
+    get_replay_log_file_mock.return_value = os.path.join(resources_directory, "mpacks_replay.log")
+    os_path_exists_mock.return_value = True
+
+    replay_mpack_logs()
+
+    install_replay_options = {
+      'purge' : True,
+      'mpack_command' : 'install-mpack',
+      'mpack_path': '/var/lib/ambari-server/resources/mpacks/cache/hdp-1.0.0.0.tar.gz',
+      'force': False,
+      'verbose': True
+    }
+
+    upgrade_replay_options = {
+      'purge' : False,
+      'mpack_command' : 'upgrade-mpack',
+      'mpack_path': '/var/lib/ambari-server/resources/mpacks/cache/hdp-1.0.0.1.tar.gz',
+      'force': True,
+      'verbose': True
+    }
+
+    install_mpack_mock.assert_has_calls([call(install_replay_options, replay_mode=True)])
+    upgrade_mpack_mock.assert_has_calls([call(upgrade_replay_options, replay_mode=True)])
+
+  def _create_empty_options_mock(self):
+    options = MagicMock()
+    options.mpack_path = None
+    options.purge = None
+    options.force = None
+    options.verbose = None
+    return options
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/common-services/MYSERVICE/1.0.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/common-services/MYSERVICE/1.0.0/metainfo.xml b/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/common-services/MYSERVICE/1.0.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/common-services/MYSERVICE/1.0.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/custom-services/MYSERVICE/1.0.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/custom-services/MYSERVICE/1.0.0/metainfo.xml b/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/custom-services/MYSERVICE/1.0.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/custom-services/MYSERVICE/1.0.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/custom-services/MYSERVICE/2.0.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/custom-services/MYSERVICE/2.0.0/metainfo.xml b/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/custom-services/MYSERVICE/2.0.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/custom-services/MYSERVICE/2.0.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/mpack.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/mpack.json b/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/mpack.json
new file mode 100644
index 0000000..faade4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/myservice-ambari-mpack-1.0.0.0/mpack.json
@@ -0,0 +1,50 @@
+{
+  "type" : "full-release",
+  "name" : "myservice-ambari-mpack",
+  "version": "1.0.0.0",
+  "description" : "MyService Management Pack",
+  "prerequisites": {
+    "min_ambari_version" : "2.4.0.0",
+    "min_stack_versions" : [
+      {
+        "stack_name" : "MYSTACK",
+        "stack_version" : "1.0"
+      }
+    ]
+  },
+  "artifacts": [
+    {
+      "name" : "MYSERVICE-service-definition",
+      "type" : "service-definition",
+      "source_dir" : "common-services/MYSERVICE/1.0.0",
+      "service_name" : "MYSERVICE",
+      "service_version" : "1.0.0"
+    },
+    {
+      "name" : "MYSERVICE-1.0.0",
+      "type" : "stack-extension-definition",
+      "source_dir": "custom-services/MYSERVICE/1.0.0",
+      "service_name" : "MYSERVICE",
+      "service_version" : "1.0.0",
+      "applicable_stacks" : [
+        {
+          "stack_name" : "MYSTACK",
+          "stack_version" : "1.0"
+        }
+      ]
+    },
+    {
+      "name" : "MYSERVICE-2.0.0",
+      "type" : "stack-extension-definition",
+      "source_dir": "custom-services/MYSERVICE/2.0.0",
+      "service_name" : "MYSERVICE",
+      "service_version" : "2.0.0",
+      "applicable_stacks" : [
+        {
+          "stack_name" : "MYSTACK",
+          "stack_version" : "2.0"
+        }
+      ]
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEA/1.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEA/1.0/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEA/1.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEA/1.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEA/2.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEA/2.0/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEA/2.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEA/2.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEB/1.0.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEB/1.0.0/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEB/1.0.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEB/1.0.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEB/2.0.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEB/2.0.0/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEB/2.0.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/common-services/SERVICEB/2.0.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/mpack.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/mpack.json b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/mpack.json
new file mode 100644
index 0000000..d987142
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/mpack.json
@@ -0,0 +1,21 @@
+{
+  "type" : "full-release",
+  "name" : "mystack-ambari-mpack",
+  "version": "1.0.0.0",
+  "description" : "HDP Management Pack",
+  "prerequisites": {
+    "min_ambari_version" : "2.4.0.0"
+  },
+  "artifacts": [
+    {
+      "name" : "mystack-service-definitions",
+      "type" : "service-definitions",
+      "source_dir": "common-services"
+    },
+    {
+      "name" : "mystack-stack-definitions",
+      "type" : "stack-definitions",
+      "source_dir": "stacks"
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.0/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.0/services/SERVICEA/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.0/services/SERVICEA/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.0/services/SERVICEA/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.0/services/SERVICEA/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.1/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.1/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.1/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.1/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.1/services/SERVICEA/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.1/services/SERVICEA/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.1/services/SERVICEA/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/1.1/services/SERVICEA/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/2.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/2.0/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/2.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/2.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/2.0/services/SERVICEA/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/2.0/services/SERVICEA/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/2.0/services/SERVICEA/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/2.0/services/SERVICEA/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/2.0/services/SERVICEB/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/2.0/services/SERVICEB/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/2.0/services/SERVICEB/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.0/stacks/MYSTACK/2.0/services/SERVICEB/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEA/1.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEA/1.0/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEA/1.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEA/1.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEA/2.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEA/2.0/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEA/2.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEA/2.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEB/1.0.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEB/1.0.0/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEB/1.0.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEB/1.0.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEB/2.0.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEB/2.0.0/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEB/2.0.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEB/2.0.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEC/1.0.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEC/1.0.0/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEC/1.0.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEC/1.0.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEC/2.0.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEC/2.0.0/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEC/2.0.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/common-services/SERVICEC/2.0.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/mpack.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/mpack.json b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/mpack.json
new file mode 100644
index 0000000..e06efb8
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/mpack.json
@@ -0,0 +1,21 @@
+{
+  "type" : "full-release",
+  "name" : "mystack-ambari-mpack",
+  "version": "1.0.0.1",
+  "description" : "HDP Management Pack",
+  "prerequisites": {
+    "min_ambari_version" : "2.4.0.0"
+  },
+  "artifacts": [
+    {
+      "name" : "mystack-service-definitions",
+      "type" : "service-definitions",
+      "source_dir": "common-services"
+    },
+    {
+      "name" : "mystack-stack-definitions",
+      "type" : "stack-definitions",
+      "source_dir": "stacks"
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.0/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.0/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.0/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.0/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.0/services/SERVICEA/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.0/services/SERVICEA/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.0/services/SERVICEA/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.0/services/SERVICEA/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.1/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.1/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.1/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.1/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/5dddc529/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.1/services/SERVICEA/metainfo.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.1/services/SERVICEA/metainfo.xml b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.1/services/SERVICEA/metainfo.xml
new file mode 100644
index 0000000..7c20d4a
--- /dev/null
+++ b/ambari-server/src/test/python/mpacks/mystack-ambari-mpack-1.0.0.1/stacks/MYSTACK/1.1/services/SERVICEA/metainfo.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<metainfo></metainfo>
\ No newline at end of file