浏览代码

AMBARI-2453. Add cleanup script to Ambari to make sure previous artifacts are cleaned up. (swagle,smohanty via smohanty)

git-svn-id: https://svn.apache.org/repos/asf/incubator/ambari/trunk@1499545 13f79535-47bb-0310-9956-ffa450edef68
Sumit Mohanty 12 年之前
父节点
当前提交
28242c07f5

+ 441 - 0
ambari-agent/src/main/python/ambari_agent/HostCleanup.py

@@ -0,0 +1,441 @@
+#!/usr/bin/env python2.6
+
+'''
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+'''
+
+import os
+import string
+import subprocess
+import logging
+import shutil
+import platform
+import ConfigParser
+import optparse
+import shlex
+import sys
+import datetime
+import AmbariConfig
+
+logger = logging.getLogger()
+configFile = "/etc/ambari-agent/conf/ambari-agent.ini"
+
+PACKAGE_ERASE_CMD_RHEL = "yum erase -y {0}"
+PACKAGE_ERASE_CMD_SUSE = "zypper remove --quiet -n {0}"
+USER_ERASE_CMD = "userdel -rf {0}"
+PROC_KILL_CMD = "kill -9 {0}"
+ALT_DISP_CMD = "alternatives --display {0}"
+ALT_ERASE_CMD = "alternatives --remove {0} {1}"
+
+REPO_PATH_RHEL = "/etc/yum.repos.d"
+REPO_PATH_SUSE = "/etc/zypp/repos.d/"
+SKIP_LIST = ["users"]
+HOST_CHECK_FILE_NAME = "hostcheck.result"
+OUTPUT_FILE_NAME = "hostcleanup.result"
+
+PACKAGE_SECTION = "packages"
+PACKAGE_KEY = "pkg_list"
+USER_SECTION = "users"
+USER_KEY = "usr_list"
+REPO_SECTION = "repositories"
+REPOS_KEY = "repo_list"
+DIR_SECTION = "directories"
+DIR_KEY = "dir_list"
+PROCESS_SECTION = "processes"
+PROCESS_KEY = "proc_list"
+ALT_SECTION = "alternatives"
+ALT_KEYS = ["symlink_list", "target_list"]
+
+# resources that should not be cleaned
+REPOSITORY_BLACK_LIST = ["ambari.repo"]
+PACKAGES_BLACK_LIST = ["ambari-server", "ambari-agent"]
+
+class HostCleanup:
+
+  def resolve_ambari_config(self):
+    try:
+      config = AmbariConfig.config
+      if os.path.exists(configFile):
+        config.read(configFile)
+        AmbariConfig.setConfig(config)
+      else:
+        raise Exception("No config found, use default")
+
+    except Exception, err:
+      logger.warn(err)
+    return config
+
+  def do_cleanup(self, argMap=None):
+    if argMap:
+      packageList = argMap.get(PACKAGE_SECTION)
+      userList = argMap.get(USER_SECTION)
+      dirList = argMap.get(DIR_SECTION)
+      repoList = argMap.get(REPO_SECTION)
+      procList = argMap.get(PROCESS_SECTION)
+      alt_map = argMap.get(ALT_SECTION)
+
+      if procList and not PROCESS_SECTION in SKIP_LIST:
+        logger.info("\n" + "Killing pid's: " + str(procList) + "\n")
+        self.do_kill_processes(procList)
+      if packageList and not PACKAGE_SECTION in SKIP_LIST:
+        logger.info("Deleting packages: " + str(packageList) + "\n")
+        self.do_erase_packages(packageList)
+      if userList and not USER_SECTION in SKIP_LIST:
+        logger.info("\n" + "Deleting users: " + str(userList))
+        self.do_delete_users(userList)
+      if dirList and not DIR_SECTION in SKIP_LIST:
+        logger.info("\n" + "Deleting directories: " + str(dirList))
+        self.do_erase_dir_silent(dirList)
+      if repoList and not REPO_SECTION in SKIP_LIST:
+        repoFiles = self.find_repo_files_for_repos(repoList)
+        logger.info("\n" + "Deleting repo files: " + str(repoFiles))
+        self.do_erase_files_silent(repoFiles)
+      if alt_map and not ALT_SECTION in SKIP_LIST:
+        logger.info("\n" + "Erasing alternatives:" + str(alt_map) + "\n")
+        self.do_erase_alternatives(alt_map)
+
+    return 0
+
+  def read_host_check_file(self, config_file_path):
+    propertyMap = {}
+    try:
+      with open(config_file_path, 'r'): pass
+    except Exception, e:
+      logger.error("Host check result not found at: " + str(config_file_path))
+      return None
+
+    try:
+      config = ConfigParser.RawConfigParser()
+      config.read(config_file_path)
+    except Exception, e:
+      logger.error("Cannot read host check result: " + str(e))
+      return None
+
+    # Initialize map from file
+    try:
+      if config.has_option(PACKAGE_SECTION, PACKAGE_KEY):
+        propertyMap[PACKAGE_SECTION] = config.get(PACKAGE_SECTION, PACKAGE_KEY).split(',')
+    except:
+      logger.warn("Cannot read package list: " + str(sys.exc_info()[0]))
+
+    try:
+      if config.has_option(USER_SECTION, USER_KEY):
+        propertyMap[USER_SECTION] = config.get(USER_SECTION, USER_KEY).split(',')
+    except:
+      logger.warn("Cannot read user list: " + str(sys.exc_info()[0]))
+
+    try:
+      if config.has_option(REPO_SECTION, REPOS_KEY):
+        propertyMap[REPO_SECTION] = config.get(REPO_SECTION, REPOS_KEY).split(',')
+    except:
+      logger.warn("Cannot read repositories list: " + str(sys.exc_info()[0]))
+
+    try:
+      if config.has_option(DIR_SECTION, DIR_KEY):
+        propertyMap[DIR_SECTION] = config.get(DIR_SECTION, DIR_KEY).split(',')
+    except:
+      logger.warn("Cannot read dir list: " + str(sys.exc_info()[0]))
+
+    try:
+      if config.has_option(PROCESS_SECTION, PROCESS_KEY):
+        propertyMap[PROCESS_SECTION] = config.get(PROCESS_SECTION, PROCESS_KEY).split(',')
+    except:
+      logger.warn("Cannot read process list: " + str(sys.exc_info()[0]))
+
+    try:
+      alt_map = {}
+      if config.has_option(ALT_SECTION, ALT_KEYS[0]):
+        alt_map[ALT_KEYS[0]] = config.get(ALT_SECTION, ALT_KEYS[0]).split(',')
+      if config.has_option(ALT_SECTION, ALT_KEYS[1]):
+        alt_map[ALT_KEYS[1]] = config.get(ALT_SECTION, ALT_KEYS[1]).split(',')
+      if alt_map:
+        propertyMap[ALT_SECTION] = alt_map
+    except:
+      logger.warn("Cannot read alternates list: " + str(sys.exc_info()[0]))
+
+    return propertyMap
+
+  def get_alternatives_desc(self, alt_name):
+    command = ALT_DISP_CMD.format(alt_name)
+    out = None
+    try:
+      p1 = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
+      p2 = subprocess.Popen(["grep", "priority"], stdin=p1.stdout, stdout=subprocess.PIPE)
+      p1.stdout.close()
+      out = p2.communicate()[0]
+      logger.debug('alternatives --display ' + alt_name + '\n, out = ' + out)
+    except:
+      logger.warn('Cannot process alternative named: ' + alt_name + ',' +\
+                  'error: ' + str(sys.exc_info()[0]))
+
+    return out
+
+  # Alternatives exist as a stack of symlinks under /var/lib/alternatives/$name
+  # Script expects names of the alternatives as input
+  # We find all the symlinks using command, #] alternatives --display $name
+  # and delete them using command, #] alternatives --remove $name $path.
+  def do_erase_alternatives(self, alt_map):
+    if alt_map:
+      alt_list = alt_map.get(ALT_KEYS[0])
+      if alt_list:
+        for alt_name in alt_list:
+          if alt_name:
+            out = self.get_alternatives_desc(alt_name)
+
+            if not out:
+              logger.warn('No alternatives found for: ' + alt_name)
+              continue
+            else:
+              alternates = out.split('\n')
+              if alternates:
+                for entry in alternates:
+                  if entry:
+                    alt_path = entry.split()[0]
+                    logger.debug('Erasing alternative named: ' + alt_name + ', '\
+                                  'path: ' + alt_path)
+
+                    command = ALT_ERASE_CMD.format(alt_name, alt_path)
+                    (returncode, stdoutdata, stderrdata) = self.run_os_command(command)
+                    if returncode != 0:
+                      logger.warn('Failed to remove alternative: ' + alt_name +
+                                  ", path: " + alt_path + ", error: " + stderrdata)
+
+      # Remove directories - configs
+      dir_list = alt_map.get(ALT_KEYS[1])
+      if dir_list:
+        self.do_erase_dir_silent(dir_list)
+
+    return 0
+
+  def do_kill_processes(self, pidList):
+    if pidList:
+      for pid in pidList:
+        if pid:
+          command = PROC_KILL_CMD.format(pid)
+          (returncode, stdoutdata, stderrdata) = self.run_os_command(command)
+          if returncode != 0:
+            logger.error("Unable to kill process with pid: " + pid + ", " + stderrdata)
+    return 0
+
+  def get_files_in_dir(self, dirPath):
+    fileList = []
+    if dirPath:
+      if os.path.exists(dirPath):
+        listdir = os.listdir(dirPath)
+        if listdir:
+          for link in listdir:
+            path = dirPath + os.sep + link
+            if not os.path.islink(path) and not os.path.isdir(path):
+              fileList.append(path)
+
+    return fileList
+
+  def find_repo_files_for_repos(self, repoNames):
+    repoFiles = []
+    osType = self.get_os_type()
+    repoNameList = []
+    for repoName in repoNames:
+      if len(repoName.strip()) > 0:
+        repoNameList.append("[" + repoName + "]")
+        repoNameList.append("name=" + repoName)
+    if repoNameList:
+      # get list of files
+      if osType == 'suse':
+        fileList = self.get_files_in_dir(REPO_PATH_SUSE)
+      elif osType == "redhat":
+        fileList = self.get_files_in_dir(REPO_PATH_RHEL)
+      else:
+        logger.warn("Unsupported OS type, cannot get repository location.")
+        return []
+
+      if fileList:
+        for filePath in fileList:
+          with open(filePath, 'r') as file:
+            content = file.readline()
+            while (content != "" ):
+              for repoName in repoNameList:
+                if content.find(repoName) == 0 and filePath not in repoFiles:
+                  repoFiles.append(filePath)
+                  break;
+              content = file.readline()
+
+    return repoFiles
+
+  def do_erase_packages(self, packageList):
+    packageStr = None
+    if packageList:
+      packageStr = ' '.join(packageList)
+      logger.debug("Erasing packages: " + packageStr)
+    if packageStr is not None and packageStr:
+      os_name = self.get_os_type()
+      command = ''
+      if os_name == 'suse':
+        command = PACKAGE_ERASE_CMD_SUSE.format(packageStr)
+      elif os_name == 'redhat':
+        command = PACKAGE_ERASE_CMD_RHEL.format(packageStr)
+      else:
+        logger.warn("Unsupported OS type, cannot remove package.")
+
+      if command != '':
+        logger.debug('Executing: '+ str(command))
+        (returncode, stdoutdata, stderrdata) = self.run_os_command(command)
+        if returncode != 0:
+          logger.warn("Erasing packages failed: " + stderrdata)
+        else:
+          logger.info("Erased packages successfully.\n" + stdoutdata)
+    return 0
+
+  def do_erase_dir_silent(self, pathList):
+    if pathList:
+      for path in pathList:
+        if path and os.path.exists(path):
+          if os.path.isdir(path):
+            try:
+              shutil.rmtree(path)
+            except:
+              logger.warn("Failed to remove dir: " + path + ", error: " + str(sys.exc_info()[0]))
+          else:
+            logger.info(path + " is a file and not a directory, deleting file")
+            self.do_erase_files_silent([path])
+        else:
+          logger.info("Path doesn't exists: " + path)
+    return 0
+
+  def do_erase_files_silent(self, pathList):
+    if pathList:
+      for path in pathList:
+        if path and os.path.exists(path):
+          try:
+            os.remove(path)
+          except:
+            logger.warn("Failed to delete file: " + path + ", error: " + str(sys.exc_info()[0]))
+        else:
+          logger.info("File doesn't exists: " + path)
+    return 0
+
+  def do_delete_users(self, userList):
+    if userList:
+      for user in userList:
+        if user:
+          command = USER_ERASE_CMD.format(user)
+          (returncode, stdoutdata, stderrdata) = self.run_os_command(command)
+          if returncode != 0:
+            logger.warn("Cannot delete user : " + user + ", " + stderrdata)
+          else:
+            logger.info("Successfully deleted user: " + user)
+    return 0
+
+  def is_current_user_root(self):
+    return os.getuid() == 0
+
+  def get_os_type(self):
+    os_info = platform.linux_distribution(
+      None, None, None, ['SuSE', 'redhat' ], 0
+    )
+    return os_info[0].lower()
+
+
+  # Run command as sudoer by default, if root no issues
+  def run_os_command(self, cmd, runWithSudo=True):
+    if runWithSudo:
+      cmd = 'sudo ' + cmd
+    logger.info('Executing command: ' + str(cmd))
+    if type(cmd) == str:
+      cmd = shlex.split(cmd)
+    process = subprocess.Popen(cmd,
+      stdout=subprocess.PIPE,
+      stdin=subprocess.PIPE,
+      stderr=subprocess.PIPE
+    )
+    (stdoutdata, stderrdata) = process.communicate()
+    return process.returncode, stdoutdata, stderrdata
+
+
+  def search_file(filename, search_path, pathsep=os.pathsep):
+    """ Given a search path, find file with requested name """
+    for path in string.split(search_path, pathsep):
+      candidate = os.path.join(path, filename)
+      if os.path.exists(candidate): return os.path.abspath(candidate)
+    return None
+
+# Copy file and save with file.# (timestamp)
+def backup_file(filePath):
+  if filePath is not None and os.path.exists(filePath):
+    timestamp = datetime.datetime.now()
+    format = '%Y%m%d%H%M%S'
+    try:
+      shutil.copyfile(filePath, filePath + "." + timestamp.strftime(format))
+    except (Exception), e:
+      logger.warn('Could not backup file "%s": %s' % (str(filePath, e)))
+  return 0
+
+def main():
+  h = HostCleanup()
+  config = h.resolve_ambari_config()
+  hostCheckFileDir = config.get('agent', 'prefix')
+  hostCheckFilePath = os.path.join(hostCheckFileDir, HOST_CHECK_FILE_NAME)
+  hostCheckResultPath = os.path.join(hostCheckFileDir, OUTPUT_FILE_NAME)
+
+  parser = optparse.OptionParser()
+  parser.add_option("-v", "--verbose", dest="verbose", action="store_false",
+    default=False, help="output verbosity.")
+  parser.add_option("-f", "--file", dest="inputfile",
+    default=hostCheckFilePath,
+    help="host check result file to read.", metavar="FILE")
+  parser.add_option("-o", "--out", dest="outputfile",
+    default=hostCheckResultPath,
+    help="log file to store results.", metavar="FILE")
+  parser.add_option("-k", "--skip", dest="skip",
+    help="(packages|users|directories|repositories|processes|alternatives)." +\
+         " separator = ,")
+
+  (options, args) = parser.parse_args()
+  # set output file
+  backup_file(options.outputfile)
+  global logger
+  logger = logging.getLogger('HostCleanup')
+  handler = logging.FileHandler(options.outputfile)
+  formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
+  handler.setFormatter(formatter)
+  logger.addHandler(handler)
+
+  # set verbose
+  if options.verbose:
+    logging.basicConfig(level=logging.DEBUG)
+  else:
+    logging.basicConfig(level=logging.INFO)
+
+  if options.skip:
+    global SKIP_LIST
+    SKIP_LIST = options.skip.splt(',')
+
+
+  is_root = h.is_current_user_root()
+  if not is_root:
+    raise RuntimeError('HostCleanup needs to be run as root.')
+
+  hostcheckfile = options.inputfile
+  propMap = h.read_host_check_file(hostcheckfile)
+
+  if propMap:
+    h.do_cleanup(propMap)
+
+  logger.info('Clean-up completed. The output is at %s' % (str(options.outputfile)))
+
+
+if __name__ == '__main__':
+  main()

+ 9 - 6
ambari-agent/src/main/python/ambari_agent/HostInfo.py

@@ -198,12 +198,15 @@ class HostInfo:
       pass
     return diskInfo
 
-  def checkFolders(self, basePaths, projectNames, dirs):
+  def checkFolders(self, basePaths, projectNames, existingUsers, dirs):
+    foldersToIgnore = []
+    for user in existingUsers:
+      foldersToIgnore.append(user['homeDir'])
     try:
       for dirName in basePaths:
         for project in projectNames:
           path = os.path.join(dirName.strip(), project.strip())
-          if os.path.exists(path):
+          if not path in foldersToIgnore and os.path.exists(path):
             obj = {}
             obj['type'] = self.dirType(path)
             obj['name'] = path
@@ -282,14 +285,14 @@ class HostInfo:
       self.etcAlternativesConf(self.DEFAULT_PROJECT_NAMES, etcs)
       dict['alternatives'] = etcs
 
-      dirs = []
-      self.checkFolders(self.DEFAULT_DIRS, self.DEFAULT_PROJECT_NAMES, dirs)
-      dict['stackFoldersAndFiles'] = dirs
-
       existingUsers = []
       self.checkUsers(self.DEFAULT_USERS, existingUsers)
       dict['existingUsers'] = existingUsers
 
+      dirs = []
+      self.checkFolders(self.DEFAULT_DIRS, self.DEFAULT_PROJECT_NAMES, existingUsers, dirs)
+      dict['stackFoldersAndFiles'] = dirs
+
       installedPackages = []
       availablePackages = []
       self.packages.allInstalledPackages(installedPackages)

+ 335 - 0
ambari-agent/src/test/python/TestHostCleanup.py

@@ -0,0 +1,335 @@
+#!/usr/bin/env python2.6
+
+'''
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+'''
+
+from unittest import TestCase
+import unittest
+from mock.mock import patch, MagicMock, call, create_autospec
+from ambari_agent import HostCleanup
+import StringIO
+import sys
+import tempfile
+import os.path
+
+PACKAGE_SECTION = "packages"
+PACKAGE_KEY = "pkg_list"
+USER_SECTION = "users"
+USER_KEY = "usr_list"
+REPO_SECTION = "repositories"
+REPOS_KEY = "pkg_list"
+DIR_SECTION = "directories"
+DIR_KEY = "dir_list"
+PROCESS_SECTION = "processes"
+PROCESS_KEY = "proc_list"
+ALT_SECTION = "alternatives"
+ALT_KEYS = ["symlink_list", "target_list"]
+ALT_ERASE_CMD = "alternatives --remove {0} {1}"
+
+class TestHostCleanup(TestCase):
+
+  def setUp(self):
+    HostCleanup.logger = MagicMock()
+    self.hostcleanup = HostCleanup.HostCleanup()
+    # disable stdout
+    out = StringIO.StringIO()
+    sys.stdout = out
+
+
+  def tearDown(self):
+    # enable stdout
+    sys.stdout = sys.__stdout__
+
+  def test_read_host_check_file_with_content(self):
+    out = StringIO.StringIO()
+    sys.stdout = out
+    tmpfile = tempfile.mktemp()
+    f = open(tmpfile,'w')
+    fileContent = """[processes]
+proc_list = 323,434
+
+[users]
+usr_list = rrdcached,ambari-qa,hive,oozie,hbase,hcat,mysql,mapred,hdfs,zookeeper,sqoop,nagios
+
+[repositories]
+repo_list = HDP-1.3.0,HDP-epel
+
+[directories]
+dir_list = /etc/hadoop,/etc/hbase,/etc/hcatalog,/tmp/hive,/tmp/nagios,/var/nagios
+
+[alternatives]
+symlink_list = hcatalog-conf,hadoop-default,hadoop-log,oozie-conf
+target_list = /etc/hcatalog/conf.dist,/usr/share/man/man1/hadoop.1.gz,/etc/oozie/conf.dist,/usr/lib/hadoop
+
+[packages]
+pkg_list = sqoop.noarch,hadoop-libhdfs.x86_64,rrdtool.x86_64,ganglia-gmond.x86_64
+
+[metadata]
+created = 2013-07-02 20:39:22.162757"""
+    f.write(fileContent)
+    f.close()
+
+    propMap = self.hostcleanup.read_host_check_file(tmpfile)
+    self.assertTrue("323" in propMap["processes"])
+    self.assertTrue("mysql" in propMap["users"])
+    self.assertTrue("HDP-epel" in propMap["repositories"])
+    self.assertTrue("/etc/hadoop" in propMap["directories"])
+    self.assertTrue("hcatalog-conf" in propMap["alternatives"]["symlink_list"])
+    self.assertTrue("/etc/oozie/conf.dist" in propMap["alternatives"]["target_list"])
+    self.assertTrue("hadoop-libhdfs.x86_64" in propMap["packages"])
+    sys.stdout = sys.__stdout__
+
+
+  @patch.object(HostCleanup.HostCleanup, 'do_erase_alternatives')
+  @patch.object(HostCleanup.HostCleanup, 'find_repo_files_for_repos')
+  @patch.object(HostCleanup.HostCleanup, 'get_os_type')
+  @patch.object(HostCleanup.HostCleanup, 'do_kill_processes')
+  @patch.object(HostCleanup.HostCleanup, 'do_erase_files_silent')
+  @patch.object(HostCleanup.HostCleanup, 'do_erase_dir_silent')
+  @patch.object(HostCleanup.HostCleanup, 'do_delete_users')
+  @patch.object(HostCleanup.HostCleanup, 'do_erase_packages')
+  def test_do_cleanup_all(self, do_erase_packages_method, do_delete_users_method,
+                      do_erase_dir_silent_method,
+                      do_erase_files_silent_method, do_kill_processes_method,
+                      get_os_type_method, find_repo_files_for_repos_method,
+                      do_erase_alternatives_method):
+    global SKIP_LIST
+    oldSkipList = HostCleanup.SKIP_LIST
+    HostCleanup.SKIP_LIST = []
+    out = StringIO.StringIO()
+    sys.stdout = out
+    propertyMap = {PACKAGE_SECTION:['abcd', 'pqrst'], USER_SECTION:['abcd', 'pqrst'],
+                   REPO_SECTION:['abcd', 'pqrst'], DIR_SECTION:['abcd', 'pqrst'],
+                   PROCESS_SECTION:['abcd', 'pqrst'],
+                   ALT_SECTION:{ALT_KEYS[0]:['alt1','alt2'], ALT_KEYS[1]:[
+                     'dir1']}}
+    get_os_type_method.return_value = 'redhat'
+    find_repo_files_for_repos_method.return_value = ['abcd', 'pqrst']
+
+    self.hostcleanup.do_cleanup(propertyMap)
+
+    self.assertTrue(do_delete_users_method.called)
+    self.assertTrue(do_erase_dir_silent_method.called)
+    self.assertTrue(do_erase_files_silent_method.called)
+    self.assertTrue(do_erase_packages_method.called)
+    self.assertTrue(do_kill_processes_method.called)
+    self.assertTrue(do_erase_alternatives_method.called)
+    do_erase_dir_silent_method.assert_called_once_with(['abcd', 'pqrst'])
+    do_erase_packages_method.assert_called_once_with(['abcd', 'pqrst'])
+    do_erase_files_silent_method.assert_called_once_with(['abcd', 'pqrst'])
+    do_delete_users_method.assert_called_once_with(['abcd', 'pqrst'])
+    do_kill_processes_method.assert_called_once_with(['abcd', 'pqrst'])
+    do_erase_alternatives_method.assert_called_once_with({ALT_KEYS[0]:['alt1',
+                                              'alt2'], ALT_KEYS[1]:['dir1']})
+
+    HostCleanup.SKIP_LIST = oldSkipList
+    sys.stdout = sys.__stdout__
+
+
+  @patch.object(HostCleanup.HostCleanup, 'do_erase_alternatives')
+  @patch.object(HostCleanup.HostCleanup, 'find_repo_files_for_repos')
+  @patch.object(HostCleanup.HostCleanup, 'get_os_type')
+  @patch.object(HostCleanup.HostCleanup, 'do_kill_processes')
+  @patch.object(HostCleanup.HostCleanup, 'do_erase_files_silent')
+  @patch.object(HostCleanup.HostCleanup, 'do_erase_dir_silent')
+  @patch.object(HostCleanup.HostCleanup, 'do_delete_users')
+  @patch.object(HostCleanup.HostCleanup, 'do_erase_packages')
+  def test_do_cleanup_default(self, do_erase_packages_method, do_delete_users_method,
+                      do_erase_dir_silent_method,
+                      do_erase_files_silent_method, do_kill_processes_method,
+                      get_os_type_method, find_repo_files_for_repos_method,
+                      do_erase_alternatives_method):
+
+    out = StringIO.StringIO()
+    sys.stdout = out
+    propertyMap = {PACKAGE_SECTION:['abcd', 'pqrst'], USER_SECTION:['abcd', 'pqrst'],
+                   REPO_SECTION:['abcd', 'pqrst'], DIR_SECTION:['abcd', 'pqrst'],
+                   PROCESS_SECTION:['abcd', 'pqrst'],
+                   ALT_SECTION:{ALT_KEYS[0]:['alt1','alt2'], ALT_KEYS[1]:[
+                     'dir1']}}
+    get_os_type_method.return_value = 'redhat'
+    find_repo_files_for_repos_method.return_value = ['abcd', 'pqrst']
+
+    self.hostcleanup.do_cleanup(propertyMap)
+
+    self.assertFalse(do_delete_users_method.called)
+    self.assertTrue(do_erase_dir_silent_method.called)
+    self.assertTrue(do_erase_files_silent_method.called)
+    self.assertTrue(do_erase_packages_method.called)
+    self.assertTrue(do_kill_processes_method.called)
+    self.assertTrue(do_erase_alternatives_method.called)
+    sys.stdout = sys.__stdout__
+
+  @patch.object(HostCleanup.HostCleanup, 'find_repo_files_for_repos')
+  @patch.object(HostCleanup.HostCleanup, 'get_os_type')
+  @patch.object(HostCleanup.HostCleanup, 'do_kill_processes')
+  @patch.object(HostCleanup.HostCleanup, 'do_erase_files_silent')
+  @patch.object(HostCleanup.HostCleanup, 'do_erase_dir_silent')
+  @patch.object(HostCleanup.HostCleanup, 'do_delete_users')
+  @patch.object(HostCleanup.HostCleanup, 'do_erase_packages')
+  def test_do_cleanup_with_skip(self, do_erase_packages_method,
+                      do_delete_users_method,
+                      do_erase_dir_silent_method,
+                      do_erase_files_silent_method, do_kill_processes_method,
+                      get_os_type_method, find_repo_files_for_repos_method):
+
+    out = StringIO.StringIO()
+    sys.stdout = out
+    propertyMap = {PACKAGE_SECTION:['abcd', 'pqrst'], USER_SECTION:['abcd', 'pqrst'],
+                   REPO_SECTION:['abcd', 'pqrst'], DIR_SECTION:['abcd', 'pqrst'],
+                   PROCESS_SECTION:['abcd', 'pqrst']}
+    get_os_type_method.return_value = 'redhat'
+    find_repo_files_for_repos_method.return_value = ['abcd', 'pqrst']
+    HostCleanup.SKIP_LIST = [PACKAGE_SECTION, REPO_SECTION]
+
+    self.hostcleanup.do_cleanup(propertyMap)
+
+    self.assertTrue(do_delete_users_method.called)
+    self.assertTrue(do_erase_dir_silent_method.called)
+    self.assertFalse(do_erase_files_silent_method.called)
+    self.assertFalse(do_erase_packages_method.called)
+    self.assertTrue(do_kill_processes_method.called)
+    do_erase_dir_silent_method.assert_called_once_with(['abcd', 'pqrst'])
+    do_delete_users_method.assert_called_once_with(['abcd', 'pqrst'])
+    do_kill_processes_method.assert_called_once_with(['abcd', 'pqrst'])
+
+    sys.stdout = sys.__stdout__
+
+
+  @patch("ConfigParser.RawConfigParser")
+  @patch("__builtin__.open")
+  def test_read_host_check_file(self, openMock, readMock):
+    out = StringIO.StringIO()
+    sys.stdout = out
+    f = MagicMock()
+    openMock.return_value = f
+
+    propertyMap = self.hostcleanup.read_host_check_file('test')
+
+    self.assertTrue(openMock.called)
+    self.assertTrue(readMock.called)
+    self.assertTrue(propertyMap.has_key(PACKAGE_SECTION))
+    self.assertTrue(propertyMap.has_key(REPO_SECTION))
+    self.assertTrue(propertyMap.has_key(USER_SECTION))
+    self.assertTrue(propertyMap.has_key(DIR_SECTION))
+    self.assertTrue(propertyMap.has_key(PROCESS_SECTION))
+
+    sys.stdout = sys.__stdout__
+
+
+  @patch.object(HostCleanup.HostCleanup, 'run_os_command')
+  @patch.object(HostCleanup.HostCleanup, 'get_os_type')
+  def test_do_earse_packages(self, get_os_type_method, run_os_command_method):
+    out = StringIO.StringIO()
+    sys.stdout = out
+
+    get_os_type_method.return_value = 'redhat'
+    run_os_command_method.return_value = (0, 'success', 'success')
+
+    retval = self.hostcleanup.do_erase_packages(['abcd', 'wxyz'])
+
+    self.assertTrue(get_os_type_method.called)
+    self.assertTrue(run_os_command_method.called)
+    run_os_command_method.assert_called_with("yum erase -y {0}".format(' '
+    .join(['abcd', 'wxyz'])))
+    self.assertEquals(0, retval)
+
+    get_os_type_method.reset()
+    run_os_command_method.reset()
+
+    get_os_type_method.return_value = 'suse'
+    run_os_command_method.return_value = (0, 'success', 'success')
+
+    retval = self.hostcleanup.do_erase_packages(['abcd', 'wxyz'])
+
+    self.assertTrue(get_os_type_method.called)
+    self.assertTrue(run_os_command_method.called)
+    run_os_command_method.assert_called_with("zypper remove --quiet -n {0}"
+    .format(' '.join(['abcd', 'wxyz'])))
+    self.assertEquals(0, retval)
+
+    sys.stdout = sys.__stdout__
+
+  @patch.object(HostCleanup.HostCleanup, 'get_files_in_dir')
+  @patch.object(HostCleanup.HostCleanup, 'get_os_type')
+  def test_find_repo_files_for_repos(self, get_os_type_method,
+                                    get_files_in_dir_method):
+    out = StringIO.StringIO()
+    sys.stdout = out
+
+    tmpfile = tempfile.mktemp()
+    fileContent = """[###]
+[aass]
+[$$]
+444]saas[333
+1122[naas]2222
+name=sd des derft 3.1
+"""
+    with open(tmpfile,'w') as file:
+      file.write(fileContent)
+    get_os_type_method.return_value = 'redhat'
+    get_files_in_dir_method.return_value = [ tmpfile ]
+
+    repoFiles = self.hostcleanup.find_repo_files_for_repos(['aass'])
+    self.assertTrue(get_files_in_dir_method.called)
+    self.assertTrue(get_os_type_method.called)
+    self.assertEquals(repoFiles, [ tmpfile ])
+
+    repoFiles = self.hostcleanup.find_repo_files_for_repos(['sd des derft 3.1'])
+    self.assertTrue(get_files_in_dir_method.called)
+    self.assertTrue(get_os_type_method.called)
+    self.assertEquals(repoFiles, [ tmpfile ])
+
+    repoFiles = self.hostcleanup.find_repo_files_for_repos(['sd des derft 3.1', 'aass'])
+    self.assertEquals(repoFiles, [ tmpfile ])
+
+    repoFiles = self.hostcleanup.find_repo_files_for_repos(['saas'])
+    self.assertEquals(repoFiles, [])
+
+    repoFiles = self.hostcleanup.find_repo_files_for_repos([''])
+    self.assertEquals(repoFiles, [])
+
+    sys.stdout = sys.__stdout__
+
+
+  @patch.object(HostCleanup.HostCleanup, 'run_os_command')
+  @patch.object(HostCleanup.HostCleanup, 'do_erase_dir_silent')
+  @patch.object(HostCleanup.HostCleanup, 'get_alternatives_desc')
+  def test_do_erase_alternatives(self, get_alternatives_desc_mock,
+                    do_erase_dir_silent_mock, run_os_command_mock):
+    out = StringIO.StringIO()
+    sys.stdout = out
+
+    get_alternatives_desc_mock.return_value = 'somepath to alternative\n'
+    run_os_command_mock.return_value = (0, None, None)
+
+    alt_map = {ALT_KEYS[0]:['alt1'], ALT_KEYS[1]:['dir1']}
+
+    self.hostcleanup.do_erase_alternatives(alt_map)
+
+    self.assertTrue(get_alternatives_desc_mock.called)
+    get_alternatives_desc_mock.called_once_with('alt1')
+    self.assertTrue(run_os_command_mock.called)
+    run_os_command_mock.called_once_with(ALT_ERASE_CMD.format('alt1', 'somepath'))
+    self.assertTrue(do_erase_dir_silent_mock.called)
+    do_erase_dir_silent_mock.called_once_with(['dir1'])
+
+    sys.stdout = sys.__stdout__
+
+if __name__ == "__main__":
+  unittest.main(verbosity=2)

+ 2 - 1
ambari-agent/src/test/python/TestHostInfo.py

@@ -200,7 +200,8 @@ class TestHostInfo(TestCase):
     path_mock.return_value = True
     hostInfo = HostInfo()
     results = []
-    hostInfo.checkFolders(["/etc/conf", "/var/lib"], ["a1", "b1"], results)
+    existingUsers = [{'name':'a1', 'homeDir':'/home/a1'}, {'name':'b1', 'homeDir':'/home/b1'}]
+    hostInfo.checkFolders(["/etc/conf", "/var/lib", "/home/"], ["a1", "b1"], existingUsers, results)
     self.assertEqual(4, len(results))
     names = [i['name'] for i in results]
     for item in ['/etc/conf/a1', '/var/lib/a1', '/etc/conf/b1', '/var/lib/b1']: